泛型和子类型化
让我们测试一下您对泛型的理解。以下代码段合法吗?
List<String> ls = new ArrayList<String>(); // 1
List<Object> lo = ls; // 2
第 1 行当然是合法的。问题中比较棘手的部分是第 2 行。这归结为问题:是List
的String
和List
的Object
。大多数人本能地回答:“可以!”
好吧,看看接下来的几行:
lo.add(new Object()); // 3
String s = ls.get(0); // 4: Attempts to assign an Object to a String!
在这里,我们别名为ls
和lo
。通过别名lo
访问ls
,String
的列表,我们可以在其中插入任意对象。结果ls
不再只保留String
了,当我们try从中获取某些东西时,我们会感到无礼。
Java 编译器当然可以防止这种情况的发生。第 2 行将导致编译时错误。
通常,如果Foo
是Bar
的子类型(子类或子interface),并且G
是某种泛型类型声明,则G<Foo>
是G<Bar>
的子类型的情况并非如此。这可能是您需要学习泛型的最困难的事情,因为它违背了我们一贯的直觉。
我们不应该假设集合不会改变。我们的本能可能使我们认为这些事情是不变的。
例如,如果汽车部门向普查局提供了驾驶员名单,这似乎是合理的。假设Driver
是Person
的子类型,我们认为List<Driver>
是List<Person>
。实际上,正在传递的是驱动程序注册表的“副本”。否则,人口普查局可能将不是驾驶员的新人添加到列表中,从而破坏 DMV 的记录。
为了应付这种情况,考虑更灵活的泛型类型很有用。到目前为止,我们看到的规则非常严格。