问题
I encountered something that bugs me when I wrote some code. I gathered the two examples in the code sample below.
The cls1
line uses a lambda expression but doesn't compile, while the cls2
line uses a method references and compiles. I know that if I'm using non generic objects, I have no issues there, but here, I'm using generics, and, more specifically, wildcards.
import java.lang.annotation.*;
import java.util.Optional;
public class MCVE {
static class Foo {}
static class Bar extends Foo {}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
static @interface Baz { Class<? extends Foo> value(); }
@Baz(Bar.class)
static class Quz {}
// Lambda expression - doesn't compile
Class<? extends Foo> cls1 = Optional.ofNullable(Quz.class.getAnnotation(Baz.class))
.map(baz -> baz.value())
.orElse(Bar.class);
// Method reference - compiles
Class<? extends Foo> cls2 = Optional.ofNullable(Quz.class.getAnnotation(Baz.class))
.map(Baz::value)
.orElse(Bar.class);
}
In functionality, both lines are doing the same. So I just don't understand what's going on under the roof that makes the use of a lambda expression fail while a method reference has no issue.
For those who'll ask, the error received while compiling is the following:
MCVE.java:25: error: incompatible types: Class<Bar> cannot be converted to Class<CAP#1>
.orElse(Bar.class);
^
where CAP#1 is a fresh type-variable:
CAP#1 extends Foo from capture of ? extends Foo
Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output
1 error
This is rather laconic and doesn't provide any extremely useful piece of information.
Also, please note that I've done my research and that this is error is not the same as in the "Similar Questions" you can see on the right panel. The interesting part here is "fresh-type variable".
回答1:
Imagine you have 3 classes:
static class Foo {}
static class Bar extends Foo {}
static class Dem extends Foo {}
Compiler will find from lambda expression:
var x1 = Optional.ofNullable(Quz.class.getAnnotation(Baz.class));
// typeof x1 = Optional<Baz>
var x2 = x1.map(baz -> baz.value())
// typeof x2 = Optional<Class<T>>, where <T extends Foo> - this is the black magic you suffer with
// E.g. it can be
// a) Optional<Class<T=Foo>> or
// b) Optional<Class<T=Bar>> or
// c) Optional<Class<T=Dem>>
var x3 = x2.orElse(Bar.class);
// In case (a) and (b) - this code should work, in case (c) it should fail.
// Without additional explicit hint (conversion) compiler reports about this issue.
When you use method reference - compiler ignores described type inference and uses original Baz type declaration, so
.map(Baz::value) // is identical to
.map(baz -> (Class<? extends Foo>) baz.value())
来源:https://stackoverflow.com/questions/37736824/incompatible-types-classbar-cannot-be-converted-to-classcap1-where-cap1-i