为什么隐藏的静态方法在 Sun JDK 6 下可以编译,但在 OpenJDK 6 和 7 下会编译失败?
以下类:
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();
}
}
在 Sun JDK 1.6.0_20(Windows 64 位,但应该没有区别)下编译,但在 Oracle JDK 1.7.0_01(同一平台)和 OpenJDK 1.6.0_20 (Ubuntu) [1] 下失败:
[ERROR] StaticMethodsDemo.java:[37,14] error: reference to make is ambiguous, both method make() in A and method <T>make() in BPrime match
为什么?通用参数(应该被删除,不是吗?)是如何导致这种明显的不匹配的。请注意,按如下方式删除泛型:
...
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));
}
也会编译并运行(因此泛型黑客不会导致任何奇怪的运行时转换错误)。
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
显然,javac6 的行为是合理的,而 javac7 则不然。
不幸的是,根据规范的文字,javac7 是正确的。
这是由于java中的万恶之源——类型擦除。动机是在不破坏任何引用旧的、非泛化的集合 API 的旧代码的情况下泛化集合 API。为了简洁起见,我们将其称为“最愚蠢的动机”。
编译
BPrime.
然后我们需要知道类
B'
中的所有方法,包括继承的方法。这取决于B'
中的方法make()
(mb) 是否隐藏方法make()
(< em>ma) 在A
中;这取决于 mb 的签名是否是 ma 的子签名。 ( http://java.sun.com/ docs/books/jls/third_edition/html/classes.html#8.4.8)子签名概念的存在也是为了服务于最愚蠢的动机。否则,我们只需要担心确定覆盖和隐藏方法时的相同签名。
但这次这不是问题。根据定义,mb 不是 ma 的子签名,因此 ma 在类
B'
中继承。因此类B'
有两个make()
方法。下一步是确定潜在适用的方法。规则说( http://java.sun .com/docs/books/jls/third_edition/html/expressions.html#15.12.2.1)
这意味着 ma 适用于表达式
BPrime.
规范解释了
所以这也是为了服务于愚蠢的动机,我们必须允许像
Then这样的无意义语法,mb和ma都适用,因此歧义。
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.