I have the following code which compiles successfully:
import java.lang.String;
import java.util.List;
import java.util.Arrays;
interface Supplier<R> {
Foo<R> get();
}
interface Foo<R> {
public R getBar();
public void init();
}
public class Main {
static private <V> void doSomething(final Supplier<? extends List<? extends V>> supplier) {
// do something
}
static public void main(String[] args) {
doSomething(new Supplier<List<Object>>(){
@Override
public Foo<List<Object>> get() {
return new Foo<List<Object>>(){
@Override
public List<Object> getBar() {
return null;
}
@Override
public void init() {
// initialisation
}
};
}
});
}
}
However, if I convert the Supplier
to the following lambda expression the code does not compile anymore:
doSomething(() -> new Foo<List<Object>>(){
@Override
public List<Object> getBar() {
return null;
}
});
The compiler error is:
Main.java:22: error: method doSomething in class Main cannot be applied to given types;
doSomething(() -> new Foo<List<Object>>(){
^
required: Supplier<? extends List<? extends V>>
found: ()->new Fo[...]; } }
reason: cannot infer type-variable(s) V
(argument mismatch; bad return type in lambda expression
<anonymous Foo<List<Object>>> cannot be converted to Foo<List<? extends V>>)
where V is a type-variable:
V extends Object declared in method <V>doSomething(Supplier<? extends List<? extends V>>)
If I change the declaration of the supplier to Supplier<? extends List<V>>
, both variants compile successfully.
I compile the code with a Java 8 compiler.
Why does the code with the lambda not compile, although it is equivalent to the non-lambda version? Is this a known/intended limitation of Java or is it a bug?
If I use:
doSomething(() -> () -> null);
It just works fine and all types are correctly inferred by the compiler.
If I try yo do:
doSomething(() -> () -> 1);
Compilation fails, and this is correct, because the doSomething
method expects a Supplier<? extends List<? extends V>>
argument and () -> () -> 1
isn't.
And if I do:
doSomething(() -> () -> Arrays.asList(1, 2, 3));
It works as expected.
So there's no need to cast anything here, just using lambdas and letting the compiler do its work is fine.
EDIT:
And if I do this:
doSomething(() -> new Foo<List<? extends Object>>() {
@Override
public List<? extends Object> getBar() {
return null;
}
});
It compiles without error.
So bottom line, the problem is that the compiler thinks that List<Object>
is not the same as a List<? extends Object>
, and when you're using lambda expressions, it just complains about it (wrongly). It doesn't complain with anonymous inner classes, though, so it all indicates this is a bug.
The issue is caused by using ? extends V
with List
in doSomething
method definition, and but when invoke method, you are using new Foo<List<Object>>
directly.
In there, List<? extends Object>
is not equal to List<Object>
, Since ? extends Object
is covariance with type Object
, for example, you can not put Object
type element into List<? extends Object>
, because compiler can't infer what's the type should be ? extends Object
.
So for your example, you can try to fix it by directly using new Foo<List<? extends Object>>()
, maybe like:
doSomething(() -> new Foo<List<? extends Object>>() {
@Override
public List<Object> getBar() {
return null;
}
});
And for why use the anonymous class can work in here, try to decompile the anonymous class,
class Main$1$1 implements Foo<java.util.List<java.lang.Object>>
...
final class Main$1 implements SupplierT<java.util.List<java.lang.Object>> {
...
as you can see, it's overriding the ? extends V
as Object
type.
but for lambda, it will not generate the corresponding anonymous class, for example:
class Main$1 implements SupplierT<java.util.List<java.lang.Object>>
the above anonymous class will not generate, and lambda will use invokedynamic
instruction directly call, as:
0: invokedynamic #5, 0 // InvokeDynamic #0:get:()LSupplierT;
So it's still try to infer ? extends V
, it will cause compile fails.
Reference:
https://docs.oracle.com/javase/tutorial/java/generics/subtyping.html
or this example:
Is List a subclass of List? Why are Java generics not implicitly polymorphic?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With