我玩弄泛型和发现,让我惊讶的是,下面的代码编译:为什么在推断数组类型时java类型不安全?
class A {}
class B extends A {}
class Generic<T> {
private T instance;
public Generic(T instance) {
this.instance = instance;
}
public T get(){ return instance; }
}
public class Main {
public static void main(String[] args) {
fArray(new B[1], new Generic<A>(new A())); // <-- No error here
}
public static <T> void fArray(T[] a, Generic<? extends T> b) {
a[0] = b.get();
}
}
我希望T
被推断为B
。 A
不延伸B
。那为什么编译器不会抱怨呢?
T
似乎被推断为Object
,因为我也可以通过Generic<Object>
。
此外,当实际运行的代码,它将引发对a[0] = b.get();
线的ArrayStoreException
。
我没有使用任何原始泛型类型。如果T
实际上被推断为B
,我觉得这个异常可以通过编译时错误或者至少一个警告来避免。
当与List<...>
等效进一步的测试:
public static void main(String[] args) {
fList(new ArrayList<B>(), new Generic<A>(new A())); // <-- Error, as expected
}
public static <T> void fList(List<T> a, Generic<? extends T> b) {
a.add(b.get());
}
这不会产生与误差:
The method fList(List<T>, Generic<? extends T>) in the type Main is not applicable for the arguments (ArrayList<B>, Generic<A>)
一样更通用的情况下:
public static <T> void fList(List<? extends T> a, Generic<? extends T> b) {
a.add(b.get()); // <-- Error here
}
的编译器正确地认识到第一个?
可能比第二个?
更靠后。
例如如果第一个?
是B
而第二个?
是A
那么这是不安全的。
那么,为什么第一个例子没有产生类似的编译器错误呢?这只是一个疏忽吗?还是有技术限制?
我可能会产生一个错误的唯一方法是通过显式地提供类型:从2005年
Main.<B>fArray(new B[1], new Generic<A>(new A())); // <-- Not applicable
我并没有真正找到过我自己的研究任何东西,除了this article(仿制药之前) ,其中谈到阵列协方差的危险。
数组协变似乎暗示了一个解释,但我想不出一个。
最新的JDK是1.8.0.0_91
我不知道你是否会碰到Java的类型删除泛型?只是一个想法,我没有时间看看更接近的ATM。 – Ukko