如何在java中创建List >?
如何在java中创建List >?
来自 Joshua Bloch 的《Effective Java》,
- 数组与通用类型有两个重要的不同之处。首先,数组是协变的。通用类型是不变的。
- 协变意味着如果 X 是 Y 的子类型,则 X[] 也将是 Y[] 的子类型。数组是协变的。因为字符串是对象的子类型,所以
String[] 是 Object[] 的子类型
不变意味着无论 X 是否是 Y 的子类型,
List will not be subType of List.
我的问题是为什么在 Java 中决定使数组协变?有其他的 SO 帖子,如《为什么数组是不变的,但列表是协变的?》,但它们似乎专注于 Scala,我无法理解。
原因是每个数组在运行时都知道其元素类型,而通用集合由于类型擦除而不知道。
例如:
String[] strings = new String[2]; Object[] objects = strings; // valid, String[] is Object[] objects[0] = 12; // error, would cause java.lang.ArrayStoreException: java.lang.Integer during runtime
如果通用集合允许这样做:
Liststrings = new ArrayList (); List
但当某人尝试访问列表时,这将会引起问题:
String first = strings.get(0); // would cause ClassCastException, trying to assign 12 to String
根据维基百科:
早期版本的Java和C#不包括泛型(即参数化多态性)。
在这样的情况下,使数组不变规则排除了有用的多态程序。
例如,考虑编写一个对数组进行洗牌的函数,或者编写一个利用Object.equals
方法测试两个数组是否相等的函数。实现不依赖于存储在数组中的确切元素类型,因此应该可以编写一个函数,它适用于所有类型的数组。易于实现以下类型的函数boolean equalArrays (Object[] a1, Object[] a2); void shuffleArray(Object[] a);
但是,如果将数组类型视为不变,则只能在确切类型为
Object []
的数组上调用这些函数。例如,无法对字符串数组进行洗牌。因此,Java和C#都将数组类型变为协变。例如,在C#中,
string [ ]
是object [ ]
的子类型,在Java中,String [ ]
是Object [ ]
的子类型。
这回答了“为什么数组是协变的?”这个问题,或者更准确地说,“为什么当时将数组变为协变?”
当引入泛型时,出于Jon Skeet的这个答案中指出的原因,它们被有意地不协变:
不,
List
不是List
。考虑您可以对List
做什么-您可以向其中添加任何动物...包括猫。现在,可以将猫逻辑地添加到一窝小狗中吗?绝对不能。// Illegal code - because otherwise life would be Bad Listdogs = new List (); List animals = dogs; // Awooga awooga animals.add(new Cat()); Dog dog = dogs.get(0); // This should be safe, right?
突然,一只非常困惑的猫出现了。
维基百科文章中描述的使数组协变的最初动机不适用于泛型,因为通配符使协变(和反变)的表达成为可能,例如:
boolean equalLists(List> l1, List> l2); void shuffleList(List> l);