Tom*_*zak 6 generics type-inference language-lawyer java-8 ecj
在我们的代码库从 java 1.7 迁移到 1.8 的过程中,我们在几个代码位置收到一条错误消息“该方法......不适用于参数”,所有代码位置都遵循相同的泛型使用模式。
我们目前主要Eclipse Mars在 上使用(4.5.2) Windows 7,但也可以通过Neon(4.6)确认行为。Javac以及ecj1.7 合规级别都可以编译我们的代码而不会出错。
这是一个最小、完整且可验证的示例:
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());
}
}
Run Code Online (Sandbox Code Playgroud)
现在切换doSomething方法中注释掉的行可以编译代码,所以我们有一个解决方法。仍然错误消息似乎不正确,因为类SubFooBar扩展Foo和实现Bar,所以它履行了 的合同<T extends Foo & Bar>,这是 中要求的<T extends Foo & Bar> FooBar<T> makeFooBar(),所以实际上TIMO 应该绑定到SubFooBar.
我搜索了类似的问题并找到了这些: 类型推断 JDK8 javac/Eclipse Luna 的差异? Eclipse 中的类型推断编译器错误,Java8 但不是 Java7
这让我觉得这可能是一个ecj错误。在这门课程中,我也研究了Eclipse Bugzilla但找不到任何可比的东西,我看到了这些:
现在Eclipse Bugzilla讨论中充满了有关ecj. 我所理解的是那里的普遍共识,Eclipse编译器必须严格遵守JLS而不是javac(在错误的情况下),所以它不一定是ecj. 如果它不是一个ecj错误,那么编译代码一定是一个javac错误。
我感兴趣的是——对于那些可以分析我的代码片段的类型推断过程的人——代码应该已经编译还是我在编码中出错了?
编辑
正如我承诺将我的报告结果发布给 Eclipse 的那样Bugzilla:该缺陷的 ID 为 #497905(Stephan Herrmann 在他的评论中发布了已接受答案下方的链接),目前针对 v4.7。
在方法中
\n\npublic void doSomething(SubFooBar value) {\n FooBar.makeFooBar().create(value);\n}\nRun Code Online (Sandbox Code Playgroud)\n\nT该方法的类型参数makeFooBar()永远不会被推断为SubFooBar。您随后将 的实例传递SubFooBar给该create方法这一事实不会影响前面的调用表达式 的类型FooBar.makeFooBar()。
这不会随着 Java\xc2\xa08\xe2\x80\x99s 目标类型的改变而改变,因为这个新功能不适用于链式方法调用的接收者表达式。
\n\nT因此在所有版本中,调用的推断类型makeFooBar()将是交集类型Foo & Bar,因此结果类型是FooBar<Foo&Bar>。这也是 Eclipse 推断的内容,即使编辑器中的工具提示可能会显示其他内容。
这意味着您可以按照预期将SubFooBar实例传递给create方法,并且扩展和实现是兼容的。FooBar<Foo&Bar>.create(\xe2\x80\xa6)Foo&BarSubFooBarFooBar
它\xe2\x80\x99s 可以证明 Eclipse 确实推断出与所有其他编译器相同的类型,因为插入了适当的类型转换
\n\npublic void doSomething(SubFooBar value) {\n FooBar.makeFooBar().create((Foo&Bar)value);\n}\nRun Code Online (Sandbox Code Playgroud)\n\n使编译器错误消失。所以这里的问题不是类型推断,而是这个 Eclipse 版本认为SubFooBar不可分配给Foo & Bar.
| 归档时间: |
|
| 查看次数: |
1736 次 |
| 最近记录: |