A.3 容易忽略的事实
许多开发人员或许惊讶于 ArrayList 和 ArrayList 并无实质性的关联。如例 A-4 所示,可以将 Object 的子类添加到 Object 集合中。
例 A-4
List的应用
- List<Object> objects = new ArrayList<Object>();
- objects.add("Hello");
- objects.add(LocalDate.now());
- objects.add(3);
- System.out.println(objects);
很好!由于 String 是 Object 的子类,可以将 String 引用赋给 Object 引用。读者可能认为,在声明字符串列表之后就能为其添加对象,但实际情况并非如此,如例 A-5 所示。
例 A-5
List与对象一起使用
- List<String> strings = new ArrayList<>();
- String s = "abc";
- Object o = s; ➊
- // strings.add(o); ➋
- // List<Object> moreObjects = strings; ➌
- // moreObjects.add(new Date());
- // String s = moreObjects.get(0); ➍
❶ 合法
❷ 不合法
❸ 同样不合法,但假设其合法
❹ 损坏的集合
由于 String 是 Object 的子类,我们可以将 String 引用赋给 Object 引用,但无法将 Object 引用添加到 List。这似乎有些奇怪,原因在于 List 并非 List 的子类。在声明类型时,可以添加的唯一实例就是所声明的类型,使用子类或超类实例均不合法。换言之,参数化类型(parameterized type)具有不变性(invariance)。
在本例中,从注释掉的语句不难看出为何 List 不是 List 的子类。假设可以将 List 赋给 List,那么通过对象引用列表就能将非字符串的内容添加到列表中。这样一来,采用字符串列表的原始引用检索时会导致强制转换异常,编译器将无法判断转换是否有效。
不过,如果定义了一个数字列表,应该就可以为列表添加整数、浮点数与双精度浮点数。为此,我们需要在类型边界(type bound)中使用通配符。
