列表优于数组

This commit is contained in:
尉勇强2 2019-11-26 19:45:04 +08:00
parent 35b55d1c36
commit 714400aaf0
5 changed files with 147 additions and 0 deletions

View File

@ -0,0 +1,28 @@
## 列表优于数组
数组在两个重要方面与泛型不同。 首先数组是协变的covariant。 这个吓人的单词意味着如果 Sub 是 Super 的子类型,则数组类型 `Sub[]` 是数组类型 `Super[]` 的子类型。 相比之下泛型是不变的invariant对于任何两种不同的类型 `Type1``Type2``List<Type1>` 既不是 `List<Type2>` 的子类型也不是父类型。 你可能认为这意味着泛型是不足的,但可以说是数组缺陷。
**示例代码**[Item28Example01.java](Generics/src/main/java/com/jueee/item28/Item28Example01.java):数组和泛型对比。
由于这些基本差异,数组和泛型不能很好地在一起混合使用。 例如,创建泛型类型的数组,参数化类型的数组,以及类型参数的数组都是非法的。 因此,这些数组创建表达式都不合法:`new List<E>[]``new List<String>[]``new E[]`。 所有将在编译时导致泛型数组创建错误。
类型 `E``List<E>` 和 `List<String>` 等在技术上被称为不可具体化的类型nonreifiable types。 直观地说,不可具体化的类型是其运行时表示包含的信息少于其编译时表示的类型。 由于擦除,可唯一确定的参数化类型是无限定通配符类型,如 `List<?>``Map<?, ?>`(详见第 26 条)。 尽管很少有用,创建无限定通配符类型的数组是合法的。
当你在强制转换为数组类型时,得到泛型数组创建错误,或是未经检查的强制转换警告时,最佳解决方案通常是使用集合类型 `List<E>` 而不是数组类型 `E[]`。 这样可能会牺牲一些简洁性或性能,但作为交换,你会获得更好的类型安全性和互操作性。
假设你想用带有集合的构造方法来编写一个 `Chooser` 类,并且有个方法返回随机选择的集合的一个元素:
- [Item28Example02.java](Generics/src/main/java/com/jueee/item28/Item28Example02.java):没有泛型的简单实现。
要使用这个类,每次调用方法时,都必须将 `Object``choose` 方法的返回值转换为所需的类型,如果类型错误,则转换在运行时失败。
- [Item28Example03.java](Generics/src/main/java/com/jueee/item28/Item28Example03.java):修改 `Chooser` 类,使其成为泛型的。
编译器告诉你在运行时不能保证强制转换的安全性,因为程序不会知道 `T` 代表什么类型。
- [Item28Example04.java](Generics/src/main/java/com/jueee/item28/Item28Example04.java):要消除未经检查的强制转换警告,请使用列表而不是数组。
总之,数组和泛型具有非常不同的类型规则。 数组是协变和具体化的; 泛型是不变的,类型擦除的。 因此,数组提供运行时类型的安全性,但不提供编译时类型的安全性,反之亦然。
一般来说,数组和泛型不能很好地混合工作。 如果你发现把它们混合在一起,得到编译时错误或者警告,你的第一个冲动应该是用列表来替换数组。

View File

@ -0,0 +1,15 @@
package com.jueee.item28;
public class Item28Example01 {
public static void main(String[] args) {
// Fails at runtime!
Object[] objectArray = new Long[1];
objectArray[0] = "I don't fit in"; // 抛出 ArrayStoreException 异常
System.out.println(objectArray[0]);
// Won't compile!
// List<Object> ol = new ArrayList<Long>(); // Incompatible types
// ol.add("I don't fit in");
}
}

View File

@ -0,0 +1,34 @@
package com.jueee.item28;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
public class Item28Example02 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
Chooser chooser = new Chooser(list);
System.out.println(chooser.choose());
}
}
// Chooser - a class badly in need of generics!
class Chooser {
private final Object[] choiceArray;
public Chooser(Collection choices) {
choiceArray = choices.toArray();
}
// 返回随机选择的集合的一个元素
public Object choose() {
Random rnd = ThreadLocalRandom.current();
return choiceArray[rnd.nextInt(choiceArray.length)];
}
}

View File

@ -0,0 +1,35 @@
package com.jueee.item28;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
public class Item28Example03 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
Chooser2<String> chooser = new Chooser2<String>(list);
System.out.println(chooser.choose());
}
}
// Chooser2 - a class badly in need of generics!
class Chooser2<T> {
private final T[] choiceArray;
public Chooser2(Collection<T> choices) {
choiceArray = (T[])choices.toArray();
}
// 返回随机选择的集合的一个元素
public Object choose() {
Random rnd = ThreadLocalRandom.current();
return choiceArray[rnd.nextInt(choiceArray.length)];
}
}

View File

@ -0,0 +1,35 @@
package com.jueee.item28;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
public class Item28Example04 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
Chooser3<String> chooser = new Chooser3<String>(list);
System.out.println(chooser.choose());
}
}
// Chooser3 - a class badly in need of generics!
class Chooser3<T> {
private final List<T> choiceArray;
public Chooser3(Collection<T> choices) {
choiceArray = new ArrayList<>(choices);
}
// 返回随机选择的集合的一个元素
public Object choose() {
Random rnd = ThreadLocalRandom.current();
return choiceArray.get(rnd.nextInt(choiceArray.size()));
}
}