当前位置: 动力学知识库 > 问答 > 编程问答 >

java - Why does a hidden static method compile under Sun JDK 6 but cause a compile failure under OpenJDK 6 and 7?

问题描述:

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 class B'. ( 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 method make() (mb) in B' hides method make() (ma) in A; 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 class B' has two make() 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 )

If the method invocation includes explicit type parameters, and the member is a generic method, then the number of actual type parameters is equal to the number of formal type parameters.

That means ma is applicable to expression BPrime.<Object>make(), because ma is not a generic method. What?!

The spec explains

The clause above implies that a non-generic method may be potentially applicable to an invocation that supplies explicit type parameters. Indeed, it may turn out to be applicable. In such a case, the type parameters will simply be ignored.

This rule stems from issues of compatibility and principles of substitutability. Since interfaces or superclasses may be generified independently of their subtypes, we may override a generic method with a non-generic one. However, the overriding (non-generic) method must be applicable to calls to the generic method, including calls that explicitly pass type parameters. Otherwise the subtype would not be substitutable for its generified supertype.

So this is also to serve the dumbest motivation, and we have to allow nonsense syntax like

    System.<String,Integer>currentTimeMillis();

Then, both mb and ma are applicable, thus the ambiguity.

分享给朋友:
您可能感兴趣的文章:
随机阅读: