问题
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
回答1:
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.
来源:https://stackoverflow.com/questions/13980552/oracle-jdk-and-eclipse-jdt-compilers-disagree-which-is-compiling-this-incorrect