Java 教程是为 JDK 8 编写的。本页中描述的示例和实践未利用在后续版本中引入的改进。
让我们测试一下你对泛型的理解。以下代码段是否合法?
List<String> ls = new ArrayList<String>(); // 1 List<Object> lo = ls; // 2
第 1 行肯定是合法的。问题的棘手部分是第 2 行。这归结为一个问题:String
的 List
是否是 Object
的 List
。大多数人本能地回答:“当然!”
那么,看看接下来的几行:
lo.add(new Object()); // 3 String s = ls.get(0); // 4: Attempts to assign an Object to a String!
这里我们记为别名 ls
和 lo
。通过别名 lo
访问 String
列表的 ls
,我们可以在其中插入任意对象。结果 ls
不再只持有 String
了,当我们尝试从中得到一些东西时,我们得到了一个粗鲁的惊喜。
Java 编译器当然会阻止这种情况发生。第 2 行将导致编译时错误。
通常,如果 Foo
是 Bar
的子类型(子类或子接口),并且 G
是一些泛型类型声明,则 G<Foo>
不是 G<Bar>
的子类型。这可能是你需要了解泛型最困难的事情,因为它违背了我们深刻的直觉。
我们不应该假设集合不会更改。我们的直觉可能会让我们认为这些事情是不可改变的。
例如,如果汽车部门向人口普查局提供司机清单,这似乎是合理的。我们认为 List<Driver>
是 List<Person>
,假设 Driver
是 Person
的子类型。事实上,传递的是注册司机的 副本。否则,人口普查局可能会将不是司机的新人加入名单,破坏 DMV 的记录。
为了应对这种情况,考虑更灵活的泛型类型是有用的。到目前为止我们看到的规则是相当严格的。