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.