1. List<Car> carList = new ArrayList<Sedan>();
2. List<Car> carList = new ArrayList<Car>();
carList.add(new Sedan());
1有编译错误,2是合法的。
为什么变量声明的类型必须与我们传递给对象类型的类型匹配(不允许派生类型)? 我正在使用如下绝对正确的数组:
int SIZE = 10;
Car[] carArray = new Sedan[SIZE];
有谁能告诉我为什么收藏品必须声明为条件2?感谢
答案 0 :(得分:5)
Sedan[]
是Car[]
的子类型。但这会对类型安全产生非常不利的影响,因为以下代码是合法的:
Sedan[] sedans = new Sedan[10];
Car[] cars = sedans; // legal, since Sedan[] extends Car[]
cars[0] = new Car(); // Houston, we have a problem
此代码在运行时导致ArrayStoreException,因为应该只包含轿车的数组将包含非轿车。
泛型的目标是拥有类型安全的集合。因此决定设计它们,以便在编译时检测上述问题,而不是运行时间。集合比数组使用得多,并且使它们具有类型安全性是非常好的。
因此他们决定List<Sedan>
不是List<Car>
。
答案 1 :(得分:1)
在你的案例1中,你实际上在做的是创建一个ArrayList
来保存各种汽车对象,然后尝试为这个变量分配一个ArrayList
轿车。这不会起作用,因为你要从大到小(轿车是轿车,但轿车可能不是轿车)。
基本上,案例1中的carList
可能无法发挥作用,因为使用泛型,任何Car都应该能够放入carList
。这意味着我可以在添加轿车后立即致电carList.add(new Compact();
。
案例2遵循这一惯例。
我喜欢在使用泛型时考虑它的方式是你在案例1中基本上创建了两种不同类型对象的实例。想象一下ArrayListCar
和ArrayListSedan
片刻,如果你愿意的话。每个人都专门采取他们的具体论点。令人困惑,我知道,因为数组会起作用,但这只是它的设计方式。
答案 2 :(得分:1)
关于Generics的类型安全。假设没关系:
List<Dog> dogs = new ArrayList<Dog>();
List<Animal> animals = dogs;
animals.add(new Cat());
你绝对可以将一个新的猫对象添加到Animal类型的列表中,但是如果动物指的是狗,你会搞砸一只带有一堆狗的猫,这是非常危险的,为了避免这种情况,类型安全泛型只允许声明类型匹配对象类型。
答案 3 :(得分:1)
此article和此article详细说明了这一点。 Josh Bloch使用PECS原理来解释主要的想法
Producer Extends > Consumer Super [PECS]
我最依赖于您如何与存储在容器中的数据(如生产者或消费者)进行交互。
另一个问题是何时检查类型安全性,运行时间或编译时间。考虑以下因素:
// array type safety errors caught at run time
Object[] objArray = new Long[3];
objArray[0] = "i'm a string";
Object notANumber = objArray[0];
// run time exception
Long number = (Long)notANumber;
// generic type errors caught at compile time
// use pecs principle in new ArrayList<...>();
List<Number> list = new ArrayList<Number>();
list.add("not a number"); // compile time error
list.add(Long.valueOf(42L));
Long wontWork = list.get(0); // compile time error
Number works = list.get(0);
答案 4 :(得分:0)
编译:
List<? extends Car> carList = new ArrayList<Sedan>();
但它不会像你期望的那样表现,如果你对泛型不够了解,结果可能会在以后混淆。你应该真正阅读一个教程,其中有很多。