4

我有一段代码在 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 在方法 1 上给出错误,但在方法 2 上没有,而 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();
    }
}

当我们为方法 1 和方法 2 提供显式参数时,我找不到任何会导致有效调用返回 Double 的参数(即,当使用 Double 参数化 Y 时,我找不到 X 的有效参数)。这是我所期望的行为,据我所知,这里的 Y 应该只能使用 Integer 进行参数化。

但是,当我们让编译器推断参数化时,Oracle JDK 和 Eclipse JDT 都允许将 Y 推断为 Double 的调用。如果您将鼠标悬停在 Eclipse 中的调用上,它甚至会显示参数化与我们手动失败的参数设置完全相同,那么为什么会有不同的行为呢?

(此时分配给新变量 bobble 和 babble 的原因是悬停文本显示不同的参数 - 出于某种原因将 Double 替换为 Object - 如果我们再次分配给 bubble。它仍然编译调用并分配给 Doubles ,所以我不知道这是为什么。)

所以,这可能是我提出的另一个相当模糊的问题,但这里的任何人都可以为我解释一下吗?

编辑:

Eclipse 的错误报告:https ://bugs.eclipse.org/bugs/show_bug.cgi?id=398011

4

1 回答 1

1

编译器似乎做了某种简化。Foo.method1andFoo.method2用两个参数声明,Xand Y,其中一个可以在推断期间确定,但X根本不使用。

所以当你调用的时候Double bobble = bar.method1() X应该计算为extends Foo<Bar, Double>,但是编译器决定去掉这个参数,因为它没有被使用。

当您明确指定方法参数时,编译器必须检查它们的正确性并按预期失败。

如果您更改这些方法中的任何一个以接受类型参数,X那么您将不会遇到这种模棱两可的情况,因为您将为编译器提供一些信息,这些信息将用于确定实际X的 .

当 Eclipse 的编译器完全没有显示错误时,Eclipse 有几个这样的错误,但javac开始抱怨不正确的方法调用。避免此类错误的最佳方法是使用显式参数,在这种情况下,大多数编译器的行为几乎相同。因此,如果您对显式参数有疑问,最好重新设计您的类。

于 2013-01-12T00:35:30.880 回答