Oracle JDK和Eclipse JDT编译器不同意!哪个编译不正确?不寻常的泛型和推理 [英] Oracle JDK and Eclipse JDT compilers disagree! Which is compiling this incorrectly? Unusual generics and inferrence

查看:145
本文介绍了Oracle JDK和Eclipse JDT编译器不同意!哪个编译不正确?不寻常的泛型和推理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一段在Oracle JDK 7和Eclipse JDT 7之间编译不一致的代码,但由于我不确定哪个编译器出错,我认为我应该在提交之前征求意见错误报告。



这是我可以用来证明不一致性的最简单的测试:

 接口Foo< S扩展Foo< S,T> ;, T> {
//是否应该编译?
public< X extends Foo< S,Y>,Y> Y method1();

//这个怎么样?
public< X extends Foo< ;?延伸S,Y>,Y> Y method2();
}

Oracle JDK在method1上给出错误,但不在method2上,而Eclipse没有问题用任何一种方法。我甚至不确定要么方法应该编译...



如果这两种方法都不应该编译开始,那么以下几点是但我认为如果我们添加以下代码,这两个编译器都会犯一个错误:

  interface Bar扩展Foo< Bar,整数> {
}

类错误{
void bug(){
Bar bar = null;
双泡;

//这些按预期失败...
bar。< Foo< Bar,Double>,Double>方法1();
栏。< Foo<扩展Bar,Double>,Double>方法2();

// ...但是这些并不是推断的参数应该是
//与上面相同
Double bobble = bar.method1();
Double babble = bar.method2();


$ / code>

当我们为method1和method2提供明确的参数化时,可以找到没有导致有效的调用,它将返回一个Double(即,当Y使用Double进行参数化时,我无法找到X有效的参数化)。这是我期望的行为,就我所见,Y在这里应该只能用Integer进行参数化。



当我们让编译器推断但是参数化,Oracle JDK和Eclipse JDT都允许Y的调用被推断为Double。如果您将鼠标悬停在Eclipse中的调用上,它甚至会显示参数化与我们的手动调用完全相同,那么为什么会出现不同的行为?在这个时候赋值给新的变量bobble和babble是悬停文本显示了不同的参数 - 如果我们再次分配给bubble,它会替换Double和Object - 它仍然编译调用和分配给Doubles,所以我不会不知道这是为什么。)



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



编辑:



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

解决方案

似乎编译器做了一些简化。 Foo.method1 Foo.method2 用两个参数声明, X Y ,其中之一可以在推断期间确定,但 X 完全不用。 / p>

因此,当您调用 Double bobble = bar.method1() X 应该按照扩展Foo 来计算,但是编译器决定放弃这个参数,因为它没有被使用。



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



如果更改这两种方法之一来接受参数类型 X ,那么你不会得到这种模糊的情况,因为你会提供一些编译器的信息,用于确定实际的 X



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


I have a piece of code which is compiling inconsistently between Oracle JDK 7 and Eclipse JDT 7, but since I'm not sure about which compiler is making the mistake(s) I thought I should ask for opinions here before submitting any bug reports.

This is the simplest test I could come up with to demonstrate the inconsistency:

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 gives an error on method1 but not method2, whereas Eclipse has no problem with either method. I'm not even certain that either method should compile...

If neither method should even compile to begin with then the following point is moot, but I feel like both compilers are making a mistake if we add the following code:

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();
    }
}

When we provide explicit parametrisations for method1 and method2, I can find none which result in a valid invocation which will return a Double (i.e. I can find no valid parametrisation for X when Y is parametrised with Double). This is the behaviour I expect, Y here should only be parametrisable with Integer as far as I can see.

When we let the compiler infer the parametrisation, though, both Oracle JDK and the Eclipse JDT allow the call with Y inferred to be Double. If you hover over the invocations in Eclipse it even shows the parametrisation to be exactly the same as our manual one which fails, so why the different behaviour?

(The reason for assignment to the fresh variables bobble and babble at this point is that the hover text shows different parametrisations - replacing Double with Object for some reason - if we assign to bubble again. It still compiles both invocations and assigns to the Doubles though, so I don't know why this is.)

So, this is perhaps another fairly vague question from me, but can anybody here shed some light onto this for me?

EDIT:

Bug report with Eclipse: https://bugs.eclipse.org/bugs/show_bug.cgi?id=398011

解决方案

It seems that the compiler does some kind of simplification. Foo.method1 and Foo.method2 are declared with two parameters, X and Y, one of which can be determined during inferring, but X is not used at all.

So when you call Double bobble = bar.method1() X should be calculated as extends Foo<Bar, Double>, but the compiler decides to drop this parameter because it is not used.

When you explicitly specify method parameters then compiler has to check their correctness and fails as expected.

If you change either of these methods to accept arguments of type X then you won't get this ambiguous situation because you'll provide some information for the compiler which will be used to determine actual X.

Eclipse had several such bugs when its compiler shows no errors at all, but javac starts complaining about incorrect method calls. The best way to avoid such errors is using explicit parameters, in this case most compilers behave almost identically. So if you have problems with explicit parameters it's better re-design your classes.

这篇关于Oracle JDK和Eclipse JDT编译器不同意!哪个编译不正确?不寻常的泛型和推理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆