简体   繁体   中英

Cast java.util.function.Function to Interface

Example

In this (simplified) example I can create my MyInterface -object by using a method reference to apply , but casting directly doesn't work.

@Test
public void testInterfaceCast(){
    Function<String, Integer> func = Integer::parseInt;

    MyInterface legal = func::apply; // works
    MyInterface illegal = func; // error
}

public interface MyInterface extends Function<String, Integer>{}

The second assignment gives the compiler error:

incompatible types: Function<String,Integer> cannot be converted to MyInterface

The question

Can I do some Generics magic, to be able to cast a Function<T, R> to an Interface?

The reason MyInterface illegal = func; doesn't work, is because func is declared as a variable of type Function<String,Integer> , which is not a sub-type of MyInterface .

The reason why MyInterface legal = func::apply; works is the following. You might expect the type of fun::apply to be Function<String,Integer> as well, but this is not the case. The type of fun::apply depends in part on what type the compiler expects. Since you use it in an assignment context, the compiler expects an expressen of type MyInterface , and therefore in that context func::apply is of type MyInterface . It is for this reason, that method reference expression can only appear in a assignment contexts, invocation contexts and casting contexts (see the Java Language Specification ).

Since Function<String,Integer> is not a sub-type of MyInterface , casting func to a MyInterface throws a ClassCastException . Therefore, the clearest way to convert a Function<String,Integer> to a MyInterface is to use a method reference, as you already did:

MyInterface legal = func::apply;

At the bottom of this is the fact that, in spite of all the pretty syntax which gives such an appearance superficially, Java has not adopted structural types; the rules of Liskov substitutability of named types hold as strictly as ever. In your real-life code you call Function.andThen , which returns some instance of Function , and definitely not an instance of MyInterface . So when you write

MyInterface legal = func::apply;

you'll get another object, which is an instance of MyInterface . By contrast, when you write

MyInterface illegal = func;

you are attempting to assign a reference to the existing instance of Function directly to illegal , which would violate Liskov substitutability even though it matches the type structurally. No trick can exist to get around this fundamentally; even if Java introduced some new syntactic sugar which would make it look like casting, the actual semanticts would still be that of conversion (evaluating to another instance).

It doesn't compile for the same reason this snippet doesn't compile:

Animal animal = new Animal();
Cat x = animal; //illegal assignment

ie you're implying that every animal can be a cat. The compiler has no evidence that the actual super-class instance will really be a cat at Runtime and therefore raises an error.

You have to assign Integer::parseInt to a MyInterface instance:

MyInterface func = Integer::parseInt;
MyInterface illegal = func;

as func is of type Function<String, Integer> (ie the super-type of MyInterface ).

Truth is that there's no way to make Integer::parseInt of type MyInterface at compile-time, unless assigning it to MyInterface variable.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM