下面的类:
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 1.6.0_20 (Windows64位,但不会有什么影响)下编译,但在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));
}
编译和运行(所以泛型hack不会导致任何奇怪的运行时类型转换错误)。
Issue 461: jclouds-core compilation fails using stock ubuntu openjdk
发布于 2011-12-24 07:04:20
显然,javac6的行为是合理的,而javac7则不是。
不幸的是,根据规范的文字,javac7是正确的。
这是由于java类型擦除中所有邪恶的根源。其动机是在不破坏引用旧的、非泛化的集合API的任何旧代码的情况下泛化集合API。为简洁起见,我们称它为最愚蠢的动机。
在编译BPrime.<Object>make()
时,首先javac需要找出包含该方法的类。这就是简单的B'
类。( http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12.1 )
然后我们需要知道类B'
中的所有方法,包括继承的方法。这归结于B'
中的方法make()
( mb )是否隐藏了A
中的方法make()
( ma );这归结为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.<Object>make()
,因为ma不是泛型方法。什么?!
规格说明
上面的子句暗示,非泛型方法可能适用于提供显式类型参数的调用。事实上,它可能会被证明是适用的。在这种情况下,类型参数将被简单地忽略。
这条规则源于兼容性和可替代原则的问题。由于接口或超类可以独立于其子类型进行泛化,因此我们可以用非泛型方法覆盖泛型方法。但是,重写(非泛型)方法必须适用于对泛型方法的调用,包括显式传递类型参数的调用。否则,子类型将无法替代其泛化的超类型。
所以这也是为了提供最愚蠢的动机,我们必须允许像这样的无意义语法
System.<String,Integer>currentTimeMillis();
然后,mb和ma都是可应用的,从而产生歧义。
https://stackoverflow.com/questions/8620606
复制相似问题