Java 8 generics 该方法...不适用于 Eclipse 中的参数
Java 8 generics The method ... is not applicable for the arguments in Eclipse
在我们的代码库从 java 1.7 迁移到 1.8 的过程中,我们在多个代码位置收到错误消息 "The method ... is not applicable for the arguments",所有代码位置都遵循相同的泛型使用模式。
我们目前主要在 Windows 7
上使用 Eclipse Mars
(4.5.2),但也可以确认 Neon
(4.6) 的行为。 Javac
以及具有 1.7 合规级别的 ecj
都可以正确编译我们的代码。
这是一个最小的、完整的、可验证的例子:
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
方法中注释掉的行可以编译代码,所以我们有一个解决方法。错误消息似乎仍然不正确,因为 class SubFooBar
扩展了 Foo
并实现了 Bar
,因此它满足了 <T extends Foo & Bar>
的约定,这是在<T extends Foo & Bar> 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
错误。在本课程中,我也研究了 Eclipse Bugzilla
但找不到任何可比的东西,我看到了这些:
- 430686 已验证修复 - 我的不是
- 440019 与标量类型有关 - 我的不是
- 492838、448793 与通配符有关——我的不是
现在 Eclipse Bugzilla
讨论充满了关于 ecj
内部运作的细节,我并不总能跟上。我的理解是那里的普遍共识,即 Eclipse
编译器必须严格遵循 JLS
而不是 javac
(在错误的情况下),所以它不一定是ecj
中的错误。如果不是 ecj
错误,那么编译代码一定是 javac
错误。
我感兴趣的是 - 对于那些可以分析我的代码片段的类型推断过程的人 - 代码应该编译还是我在编码时出错?
编辑
正如我承诺 post 我向 Eclipse Bugzilla
报告的结果:缺陷的 ID #497905(Stephan Herrmann post 编辑了 link在接受的答案下方的评论中),目前针对 v4.7。
方法中
public void doSomething(SubFooBar value) {
FooBar.makeFooBar().create(value);
}
方法 makeFooBar()
的类型参数 T
永远不会被推断为 SubFooBar
。之后您要将 SubFooBar
的实例传递给 create
方法这一事实不会影响前面调用表达式 FooBar.makeFooBar()
.
的类型
这不会随着 Java 8 的目标类型而改变,因为这个新功能不适用于链式方法调用的接收者表达式。
因此在所有版本中,makeFooBar()
调用的 T
推断的类型将是交集类型 Foo & Bar
,因此结果类型为 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 & Bar
.
在我们的代码库从 java 1.7 迁移到 1.8 的过程中,我们在多个代码位置收到错误消息 "The method ... is not applicable for the arguments",所有代码位置都遵循相同的泛型使用模式。
我们目前主要在 Windows 7
上使用 Eclipse Mars
(4.5.2),但也可以确认 Neon
(4.6) 的行为。 Javac
以及具有 1.7 合规级别的 ecj
都可以正确编译我们的代码。
这是一个最小的、完整的、可验证的例子:
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
方法中注释掉的行可以编译代码,所以我们有一个解决方法。错误消息似乎仍然不正确,因为 class SubFooBar
扩展了 Foo
并实现了 Bar
,因此它满足了 <T extends Foo & Bar>
的约定,这是在<T extends Foo & Bar> 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
错误。在本课程中,我也研究了 Eclipse Bugzilla
但找不到任何可比的东西,我看到了这些:
- 430686 已验证修复 - 我的不是
- 440019 与标量类型有关 - 我的不是
- 492838、448793 与通配符有关——我的不是
现在 Eclipse Bugzilla
讨论充满了关于 ecj
内部运作的细节,我并不总能跟上。我的理解是那里的普遍共识,即 Eclipse
编译器必须严格遵循 JLS
而不是 javac
(在错误的情况下),所以它不一定是ecj
中的错误。如果不是 ecj
错误,那么编译代码一定是 javac
错误。
我感兴趣的是 - 对于那些可以分析我的代码片段的类型推断过程的人 - 代码应该编译还是我在编码时出错?
编辑
正如我承诺 post 我向 Eclipse Bugzilla
报告的结果:缺陷的 ID #497905(Stephan Herrmann post 编辑了 link在接受的答案下方的评论中),目前针对 v4.7。
方法中
public void doSomething(SubFooBar value) {
FooBar.makeFooBar().create(value);
}
方法 makeFooBar()
的类型参数 T
永远不会被推断为 SubFooBar
。之后您要将 SubFooBar
的实例传递给 create
方法这一事实不会影响前面调用表达式 FooBar.makeFooBar()
.
这不会随着 Java 8 的目标类型而改变,因为这个新功能不适用于链式方法调用的接收者表达式。
因此在所有版本中,makeFooBar()
调用的 T
推断的类型将是交集类型 Foo & Bar
,因此结果类型为 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 & Bar
.