Using lambda impedes inference of type variable

  • A+
Category:Languages

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?

 


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?

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: