Jor*_*nee 12 java arrays generics type-inference type-safety
我正在玩泛型,并发现,令我惊讶的是,以下代码编译:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
我希望T被推断B.A不延伸B.那么编译器为什么不抱怨呢?
T似乎是推断Object,因为我也可以通过Generic<Object>.
而且,当实际运行代码时,它会抛出一个ArrayStoreException就a[0] = b.get();行了.
我没有使用任何原始泛型类型.我觉得这个异常可以通过编译时错误来避免,或者至少是一个警告,如果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());
}
Run Code Online (Sandbox Code Playgroud)
这确实产生了错误:
The method fList(List<T>, Generic<? extends T>) in the type Main is not applicable for the arguments (ArrayList<B>, Generic<A>)
Run Code Online (Sandbox Code Playgroud)
和更通用的情况一样:
public static <T> void fList(List<? extends T> a, Generic<? extends T> b) {
a.add(b.get()); // <-- Error here
}
Run Code Online (Sandbox Code Playgroud)
编译器正确地识别出第一个?可能比继承层次结构更低?.
例如,如果第一个?是B和第二个?是A那么这不是类型安全的.
那么为什么第一个例子不会产生类似的编译错误呢?这只是一个疏忽吗?还是有技术限制?
我可以产生错误的唯一方法是明确提供一个类型:
Main.<B>fArray(new B[1], new Generic<A>(new A())); // <-- Not applicable
Run Code Online (Sandbox Code Playgroud)
我没有通过自己的研究找到任何东西,除了2005年的这篇文章(仿制药之前),它讨论了数组协方差的危险.
数组协方差似乎暗示了解释,但我想不出一个.
当前的jdk是1.8.0.0_91
考虑这个例子:
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) {
List<T> list = new ArrayList<>();
list.add(a[0]);
list.add(b.get());
System.out.println(list);
}
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,用于推断类型参数的签名是相同的,唯一不同的是fArray()只读取数组元素而不是编写它们,使得T -> A推理在运行时完全合理.
并且编译器无法告诉您在执行该方法时将使用哪个数组引用.