And*_*ips 17 java generics java-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();
}
}
Run Code Online (Sandbox Code Playgroud)
在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
Run Code Online (Sandbox Code Playgroud)
为什么?通用参数(应该擦除,不是?)如何导致这种明显的不匹配.请注意删除泛型如下:
...
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));
}
Run Code Online (Sandbox Code Playgroud)
编译和运行(因此泛型黑客不会导致任何奇怪的运行时转换错误).
irr*_*ble 12
显然,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',包括继承的方法.这归结为是否 隐藏方法(ma)中的方法make()(mb); 归结为mb的签名是否是ma的子签名.(http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.4.8)B'make()A
子签名概念的存在也是为了服务于最愚蠢的动机.否则,我们只需要在确定覆盖和隐藏方法时担心相同的签名.
但这次不是问题.根据定义,mb不是ma的子签名,因此ma在类中继承B'.所以班B'有两种make()方法.
下一步是确定可能适用的方法.规则说(http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12.2.1)
如果方法调用包含显式类型参数,并且成员是泛型方法,则实际类型参数的数量等于正式类型参数的数量.
这意味着毫安适用于表达BPrime.<Object>make(),因为毫安是不是一个通用的方法.什么?!
规范解释道
上面的条款暗示非泛型方法可能适用于提供显式类型参数的调用.实际上,它可能会变得适用.在这种情况下,将简单地忽略类型参数.
该规则源于兼容性和可替代性原则的问题.由于接口或超类可以独立于其子类型进行泛化,因此我们可以使用非泛型方法覆盖泛型方法.但是,重写(非泛型)方法必须适用于对泛型方法的调用,包括显式传递类型参数的调用.否则,子类型不能替代其生成的超类型.
所以这也是为了服务于最愚蠢的动机,我们必须允许无意义的语法
System.<String,Integer>currentTimeMillis();
Run Code Online (Sandbox Code Playgroud)
然后,mb和ma都适用,因此模糊.
| 归档时间: |
|
| 查看次数: |
1149 次 |
| 最近记录: |