首页 > 解决方案 > ArrayList 中的类型擦除> 通用调用

问题描述

有人可以解释为什么以下代码无法编译:

ArrayList<List<?>> arrayList = new ArrayList<List<String>>();

为什么上面的代码无效,但这个运行良好:

ArrayList<?> items = new ArrayList<List<String>>();

标签: javagenericstypeserasure

解决方案


让我们从简单的列表开始。Integer 是 Number 的子类,因此您可以将 Integer 分配给 Number:

Integer i = 42;
Number n = i;  // Ok

现在,让我们来一个List<Number>. 我们不应该在那里放一个List<Integer>吗?

List<Number> numbers = new ArrayList<Integer>(); // Fails

不,它失败了。为什么?因为 Java 中的泛型是不变的,这意味着对于任何两种不同的类型 T1 和 T2 都不List<T1>是 T1 和 T2 之间的关系的子类型,List<T2>也不List<T2>是T2 的子类型。List<T1>好的,这正式解释了为什么上面的赋值无效,但这背后的逻辑是什么?

应该能够容纳任何数字,这List<Number>意味着如果我们有一个声明

List<Number> numbers;

那么我们应该可以做到number.add(Integer(42))number.add(Double(Math.PI))和其他任何事情一样number.add(subClassOfNumber)。但是,如果上述赋值

List<Number> numbers = new ArrayList<Integer>();  // ?

将是有效的,那么numbers现在将持有列表,该列表只能存储整数,因此numbers.add(anyNumberButInteger)会失败,这违反了List<Number>.

可以创建一个列表,该列表将包含 Number 的某个子类的实例:

List<? extends Number> list = Arrays.asList(5, 6, 7, 8);  // Ok

您可以毫无问题地阅读此列表:

System.out.println(list.get(2));   // 7

但是,除了 null 之外,您不能在其中放置任何内容,因为在编译时无法知道列表元素的确切类型,因此无法进行安全检查:

List<? extends Number> list = ThreadLocalRandom.current().nextBoolean() ? 
                                   new ArrayList<Integer>() : 
                                   new ArrayList<Double>();
list.add(null);      // Ok
list.add(Double.valueOf(3.1415));    //  Fails, because we don't know if Double(3.1415) is actually compatible with elements of the list

好吧,现在以您的示例为例:

List<List<?>> listOfLists = new ArrayList<List<String>>();

为了使这个赋值起作用,你实际上需要在左边有 a List<? extends List<String>>,但由于 Java 泛型是不变的,唯一扩展的类型List<String>List<String>它本身,所以唯一有效的组合是

List<List<String>> listOfListsOfStrings = new ArrayList<List<String>>();

可以使用原始类型创建一个表示列表的变量,该列表由一些列表组成:

List<? extends List> listOfLists = new ArrayList<List<String>>(); // Compiles, but loses generics. DON'T DO THIS!
List<String> listOfStrings = listOfLists.get(0);                  // Compiles, even would work as expected assuming we populated listOfLists correctly
List<Integer> listOfIntegers = listOfLists.get(0);                // Compiles, but does not work as expected (fail at runtime when accessing elements)

TLDR:唯一超级/扩展的通用类List<String>List<String>,因此List<some class which extends List<String>>自动表示List<List<String>>


推荐阅读