使用lambda阻止类型变量的推断

Bru*_*nna 13 java generics lambda java-8

我有以下代码成功编译:

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
                   }
               };
            }
       });
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,如果我转换Supplier为以下lambda表达式,代码将不再编译:

doSomething(() -> new Foo<List<Object>>(){
    @Override
    public List<Object> getBar() {
        return null;
    }
});
Run Code Online (Sandbox Code Playgroud)

编译器错误是:

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>>)
Run Code Online (Sandbox Code Playgroud)

如果我将供应商的声明更改为Supplier<? extends List<V>>,则两个变体都会成功编译.

我用Java 8编译器编译代码.

为什么lambda的代码不能编译,虽然它等同于非lambda版本?这是Java的已知/预期限制还是错误?

Fed*_*ner 4

如果我使用:

doSomething(() -> () -> null);
Run Code Online (Sandbox Code Playgroud)

它工作正常,并且编译器可以正确推断所有类型。

如果我尝试你做:

doSomething(() -> () -> 1);
Run Code Online (Sandbox Code Playgroud)

编译失败,这是正确的,因为该doSomething方法需要一个Supplier<? extends List<? extends V>>参数,但() -> () -> 1实际上没有。

如果我这样做:

doSomething(() -> () -> Arrays.asList(1, 2, 3));
Run Code Online (Sandbox Code Playgroud)

它按预期工作。

所以这里不需要强制转换任何东西,只需要使用 lambda 并让编译器完成它的工作就可以了。


编辑:

如果我这样做:

doSomething(() -> new Foo<List<? extends Object>>() {
    @Override
    public List<? extends Object> getBar() {
        return null;
    }
});
Run Code Online (Sandbox Code Playgroud)

它编译没有错误。

所以最重要的是,问题是编译器认为List<Object>与 a 不同List<? extends Object>,并且当您使用 lambda 表达式时,它只是抱怨它(错误地)。不过,它不会抱怨匿名内部类,因此这一切都表明这是一个错误。

  • @Federico 当然,这些东西是“相同的”,而不是同时的。一个只是生产者,所以编译器可能会以某种方式围绕它做一些事情...不过,看起来像是一个错误,因为它在 java-10 及更高版本中使用 `&lt;&gt;` 进行编译 (2认同)