Java 8泛型方法…不适用于Eclipse中的参数

在我们的代码库从
java 1.7迁移到1.8期间,我们在几个代码位置上得到了一条错误消息“方法……不适用于参数”,所有这些都遵循泛型使用中的相同模式.

我们目前在Windows 7上主要使用Eclipse Mars(4.5.2),但也可以确认Neon(4.6)的行为. Javac以及1.7合规级别的ecj都可以编译我们的代码而不会出错.

这是一个Minimal,Complete和Verifiable示例:

public class ComplexInterfaceTest {

  public static class Foo {}

  public interface Bar {
    void print();
  }

  public static class SubFooBar extends Foo implements Bar {
    public void print() {
      System.out.println(this.getClass().getSimpleName());
    }
  }

  public static class FooBar<T extends Foo & Bar> {
    public static <T extends Foo & Bar> FooBar<T> makeFooBar() {
      return new FooBar<>();
    }

    public void create(T value) {
      value.print();
      return;
    }
  }

  public static class Base<T extends Foo> {}

  public static class Subclass extends Base<SubFooBar> {
    public void doSomething(SubFooBar value) {
//      FooBar.<SubFooBar>makeFooBar().create(value);
      FooBar.makeFooBar().create(value);
    }
  }

  public static void main(String[] args) {
    new Subclass().doSomething(new SubFooBar());
  }

}

现在,在doSomething方法中切换注释掉的行会使代码编译,因此我们确实有一个解决方法.仍然错误消息似乎不正确,因为类SubFooBar扩展Foo并实现Bar,所以它履行< T extends Foo&的合同. Bar>,< T extends Foo&酒吧及GT; FooBar的< T> makeFooBar(),所以实际上T IMO应该绑定到SubFooBar. 我搜索了类似的问题,发现了这些:
Differences in type inference JDK8 javac/Eclipse Luna?
Type Inference Compiler Error In Eclipse with Java8 but not with Java7

这让我觉得它可能是一个ecj bug.在本课程中,我也查看了Eclipse Bugzilla,但找不到任何可比性,我看到了这些:

> 430686经过验证固定 – 我的不是
> 440019与标量类型有关 – 我的不是
> 492838,448793与通配符有关 – 我的不是

现在Eclipse Bugzilla的讨论充满了关于ecj内部工作的细节,我并不总是可以遵循.我所理解的是那里的普遍共识,Eclipse编译器必须严格遵循JLS而不是javac(在它出错的情况下),因此它不一定是ecj中的错误.如果它不是一个ecj bug,那么编译代码肯定是一个javac bug.

我感兴趣的是 – 对于那些可以分析我的代码片段的类型推断过程的人 – 如果代码已编译或我在编码时犯了错误?

编辑

正如我承诺将我的报告结果发布到Eclipse的Bugzilla:缺陷的ID为#497905(Stephan Herrmann在他的评论中发布了接受答案的链接)并且目前针对v4.7.

最佳答案 在方法中

public void doSomething(SubFooBar value) {
  FooBar.makeFooBar().create(value);
}

方法makeFooBar()的类型参数T永远不会被推断为SubFooBar.事实上,您之后将SubFooBar的实例传递给create方法,这不会影响前面的调用表达式FooBar.makeFooBar()的类型.

这不会随Java 8的目标类型而改变,因为这个新功能不适用于链式方法调用的接收器表达式.

所以在所有版本中,makeFooBar()调用的T的推断类型将是交集类型Foo&条形图,因此结果类型为FooBar< Foo& Bar>.这也是Eclipse推断的内容,即使编辑器中的工具提示可能显示其他内容.

这意味着您可以将SubFooBar实例传递给create方法,因为FooBar< Foo& Bar> .create(…)期望Foo& Bar和SubFooBar的实例扩展Foo并实现Bar兼容.

可以证明Eclipse确实推断出与所有其他编译器相同的类型,如插入适当的类型转换

public void doSomething(SubFooBar value) {
  FooBar.makeFooBar().create((Foo&Bar)value);
}

使编译器错误消失.所以这里的问题不是类型推断,而是这个Eclipse版本认为SubFooBar不能赋予Foo&酒吧.

点赞