编译器还会在定义时警告您:
List<String>[] foo = new ArrayList[3];
这是因为通过它的警告,编译器试图让您知道存储在其中List<String>[] foo
的列表可能不是泛型类型String
。你总是可以在运行时得到一个List<Integer>
in :foo
void main(){
List<String>[] array = new List[1];
fill(array);
// The compiler could not check the array at compile time, so
// the next line is permitted. But you just created yourself a time bomb.
List<String> list = array[0];
// The next line will finally cause a ClassCastException at runtime!
// Good luck with debugging, especially if you have passed your list
// arround for a while.
// If you had defined List<?>[] foo, this assignment would have been
// forbidden without explicit casting to String. But at least the "type"
// of the array did not make you false promises about the generic type
// of the list.
String value = list.get(0);
}
// This method receives an array of lists. At runtime the generic type of
// the array is gone. Therefore, a call to this method can be called with
// anz List<SomeType>[] array as an argument.
void fill(List<?>[] array) {
List<Integer> list = new ArrayList<Integer>();
list.add(123)
array[0] = list;
}
在 Java 中,泛型类型的数组没有泛型表示。相反,所有类型的数组List[]
(在字节码中,类型被称为[java.util.List
)共享一个单一的表示,无论是它List<String>
还是List<Integer>
其他任何东西。这是出于向后兼容性的原因以及数组在 Java 字节码中的表示方式。换句话说,编译器无法将数组实例标记为仅接受例如List<String>
对象。相反,编译器只能将数组标记为接受List
Java 5 之前的子类型。
通过说明
List<?>[] foo = new ArrayList[3];
你基本上告诉编译器你知道编译器无法检查任何东西,除了List
由 表示的数组中的所有 sfoo
都是一些Object
当然是微不足道的子类型。(?
等同于? extends Object
。)或者换句话说,当使用通配符时?
,您要求编译器确保 aList
包含在foo
任何泛型类型。如前所述,由于这个需求是微不足道的,编译器实际上可以释放这个需求并且不会产生警告。
现在问题来了:
class MyClass<T extends Number> { }
你仍然不能说:
MyClass<? extends Number>[] foo = new MyClass[3];
没有编译器警告。为什么?我不知道。它应该是类型最安全的声明。当看到同义声明时,这更没有意义
MyClass<?>[] foo = new MyClass[3];
被接受。出于这个原因,我假设编译器只是在完全涉及数组时跳过泛型类型变量的类型检查,但是通过确保<?>
与完全缺乏泛型的遗留代码相比,确保您认识到检查泛型数组类型是不可能的类型。