The following class:
public class StaticMethodsDemo {
public static class A {
public static A make() { return new A(); };
}
public static class B extends A {
public static B make() { return new B(); };
}
public static class BPrime<T> extends A {
public static <T> BPrime<T> make() { return new BPrime<T>(); };
}
public static void main(String[] args) {
B.make();
// compiles under Sun JDK 1.6.0_20 but fails under Oracle JDK 1.7.0_01. Why?
BPrime.<Object>make();
}
}
compiles under Sun JDK 1.6.0_20 (Windows 64-bit, but shouldn’t make a difference) but fails under Oracle JDK 1.7.0_01 (same platform) and OpenJDK 1.6.0_20 (Ubuntu) [1] with:
[ERROR] StaticMethodsDemo.java:[37,14] error: reference to make is ambiguous, both method make() in A and method <T>make() in BPrime match
Why? How does the generic parameter (which should be erased, no?) cause this apparent mismatch. Note that removing generics as follows:
...
public static class BPrime<T> extends A {
T val;
public static BPrime<?> make() { return new BPrime<Object>(); };
public void setT(T val) { this.val = val; }
}
public static void main(String[] args) {
B.make();
BPrime<Long> bprime = (BPrime<Long>) BPrime.make();
bprime.setT(Long.valueOf(10));
}
compiles and runs too (so the generics hack doesn’t cause any weird runtime casting errors).
Issue 461: jclouds-core compilation fails using stock ubuntu openjdk
Obviously, the javac6’s behavior is reasonable, and javac7’s not.
Unfortunately, according to the letter of the spec, javac7 is right.
This is due to the root of all evil in java – type erasure. The motivation is to generify collection APIs without breaking any old code that reference the old, non-generified collection API. For the purpose of brevity let’s refer to it as the dumbest motivation.
When compiling
BPrime.<Object>make(), first javac needs to figure out the class containing the method. That is easily classB'. ( http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12.1 )Then we need to know all methods in class
B', including inherited ones. That comes down to whether methodmake()(mb) inB'hides methodmake()(ma) inA; which comes down to whether signature of mb is a subsignature of ma. ( http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.4.8 )The existence of subsignature concept is also to serve the dumbest motivation. Otherwise we only need to worry about same signatures in determining overriding and hiding methods.
But that’s not a problem this time. Per definition, mb is not a subsignature of ma, so ma is inherited in class
B'. So classB'has twomake()methods.Next step, is to identify potentially applicable methods. The rule says ( http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12.2.1 )
That means ma is applicable to expression
BPrime.<Object>make(), because ma is not a generic method. What?!The spec explains
So this is also to serve the dumbest motivation, and we have to allow nonsense syntax like
Then, both mb and ma are applicable, thus the ambiguity.