我有一段代码在 Oracle JDK 7 和 Eclipse JDT 7 之间编译不一致,但由于我不确定哪个编译器出错,我想我应该在提交任何代码之前在这里征求意见错误报告。
这是我能想出的最简单的测试来证明不一致:
interface Foo<S extends Foo<S, T>, T> {
// should this compile?
public <X extends Foo<S, Y>, Y> Y method1();
// what about this?
public <X extends Foo<? extends S, Y>, Y> Y method2();
}
Oracle JDK 在 method1 上给出了一个错误,但在 method2 上没有,而 Eclipse 对这两个方法都没有问题。我什至不确定任一个方法是否应该编译...
如果这两个方法都不应该编译开始,那么下面的观点就没有实际意义了,但我觉得如果我们添加以下代码,两个编译器都会犯错误:
interface Bar extends Foo<Bar, Integer> {
}
class Bug {
void bug() {
Bar bar = null;
Double bubble;
// these fail as expected...
bar.<Foo<Bar, Double>, Double> method1();
bar.<Foo<? extends Bar, Double>, Double> method2();
// ...but these don't even though the inferred parametrisations should be
// the same as above
Double bobble = bar.method1();
Double babble = bar.method2();
}
}
当我们为 method1 和 method2 提供显式参数化时,我找不到任何会导致返回 Double 的有效调用的方法(即,当 Y 用 Double 参数化时,我找不到 X 的有效参数化)。这是我期望的行为,据我所知,这里的 Y 应该只能用 Integer 进行参数化。
但是,当我们让编译器推断参数化时,Oracle JDK 和 Eclipse JDT 都允许将 Y 推断为 Double 的调用。如果您将鼠标悬停在 Eclipse 中的调用上,它甚至会显示参数化与我们的手动调用完全相同,但失败了,那么为什么会有不同的行为呢?
(此时分配给新变量 bobble 和 babble 的原因是悬停文本显示不同的参数化——出于某种原因将 Double 替换为 Object——如果我们再次分配给 bubble。它仍然编译两个调用并分配给虽然是 double ,所以我不知道这是为什么。)
所以,这可能是我提出的另一个相当模糊的问题,但是这里的任何人都可以为我阐明这个问题吗?
编辑:
Eclipse 错误报告:https://bugs.eclipse.org/bugs/show_bug.cgi?id=398011
最佳答案
编译器似乎做了某种简化。 Foo.method1
和 Foo.method2
用两个参数声明,X
和 Y
,其中之一可以在推理过程中确定,但是 X
根本没有使用。
所以当你调用Double bobble = bar.method1()
X
应计算为 extends Foo<Bar, Double>
, 但编译器决定删除此参数,因为它未被使用。
当您显式指定方法参数时,编译器必须检查它们的正确性并按预期失败。
如果您更改这些方法中的任何一个以接受类型为 X
的参数那么您就不会遇到这种模棱两可的情况,因为您将为编译器提供一些信息,这些信息将用于确定实际 X
.
Eclipse 在其编译器完全没有显示错误时有几个这样的错误,但是 javac
开始提示不正确的方法调用。避免此类错误的最佳方法是使用显式参数,在这种情况下,大多数编译器的行为几乎相同。因此,如果您对显式参数有疑问,最好重新设计您的类。
关于generics - Oracle JDK 和 Eclipse JDT 编译器不同意!哪个编译不正确?不寻常的泛型和推理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13980552/