|
3 | 3 |
|
4 | 4 | ### 数组
|
5 | 5 |
|
6 |
| -- 在`Java`中对列表和数组的处理进行比较是有益的,同时牢记替换原则和获取和放置原则。 |
7 |
| -- 在`Java`中,数组的子类型是协变的,这意味着当`S`是`T`的子类型时,类型`S []`被认为是`T []`的一个子类型。 |
8 |
| -考虑下面的代码片段,它分配一个整数数组,分配一个数组 的数字,然后尝试在数组中分配一个`double`: |
| 6 | +在 `Java` 中对列表和数组的处理进行比较是有益的,同时牢记替换原则和获取和放置原则。 |
| 7 | + |
| 8 | +在 `Java` 中,数组的子类型是协变的,这意味着当 `S` 是 `T` 的子类型时,类型 `S []` 被认为是 `T []` 的一个子类型。考虑下面的代码片段,它分配一个整数 |
| 9 | +数组,分配一个数组 的数字,然后尝试在数组中分配一个 `double`: |
9 | 10 |
|
10 | 11 | ```java
|
11 | 12 | Integer[] ints = new Integer[] {1,2,3};
|
12 | 13 | Number[] nums = ints;
|
13 | 14 | nums[2] = 3.14; // array store exception
|
14 | 15 | assert Arrays.toString(ints).equals("[1, 2, 3.14]"); // uh oh!
|
15 | 16 | ```
|
16 |
| -- 这个程序有什么问题,因为它把一个整数数组放到一个`double`中!哪里有问题? |
17 |
| -由于`Integer []`被认为是`Number []`的子类型,所以根据替换原则, |
18 |
| -第二行的赋值必须是合法的。 相反,问题出现在第三行,并在运行时被捕获。 |
19 |
| -当一个数组被分配时(如在第一行),它被标记为它的被指定的类型(它的组件类型的运行时表示, |
20 |
| -在这个例子中是`Integer`),并且每次数组被分配到 第三行),如果指定的类型与指定的值不兼容, |
21 |
| -则会引发数组存储异常(在这种情况下,`double`不能存储到`Integer`数组中)。 |
| 17 | + |
| 18 | +这个程序有什么问题,因为它把一个整数数组放到一个 `double` 中!哪里有问题? 由于 `Integer []` 被认为是 `Number []` 的子类型,所以根据替换原则,第二 |
| 19 | +行的赋值必须是合法的。 相反,问题出现在第三行,并在运行时被捕获。 当一个数组被分配时(如在第一行),它被标记为它的被指定的类型(它的组件类型的运行时 |
| 20 | +表示,在这个例子中是 `Integer`),并且每次数组被分配到 第三行),如果指定的类型与指定的值不兼容,则会引发数组存储异常(在这种情况下,`double` 不能存 |
| 21 | +储到 `Integer` 数组中)。 |
22 | 22 |
|
23 |
| -- 相比之下,泛型的子类型关系是不变的,意味着类型`List<S>`不被认为是`List<T>`的子类型, |
24 |
| -除了`S`和`T`相同的普通情况。 这是一个类似于前一个的代码片段,用列表替换数组: |
| 23 | +相比之下,泛型的子类型关系是不变的,意味着类型 `List<S>` 不被认为是 `List<T>` 的子类型,除了 `S` 和 `T` 相同的普通情况。 这是一个类似于前一个的代 |
| 24 | +码片段,用列表替换数组: |
25 | 25 |
|
26 | 26 | ```java
|
27 | 27 | List<Integer> ints = Arrays.asList(1,2,3);
|
28 | 28 | List<Number> nums = ints; // compile-time error
|
29 | 29 | nums.set(2, 3.14);
|
30 | 30 | assert ints.toString().equals("[1, 2, 3.14]"); // uh oh!
|
31 | 31 | ```
|
32 |
| -- 由于`List<Integer>`不被认为是`List<Number>`的子类型, |
33 |
| -因此在第二行而不是第三行检测到问题,并且在编译时检测到,而不是在运行时检测到。 |
34 | 32 |
|
35 |
| -- 通配符重新引入泛型的协变子类型,在当S是T的子类型时,这种类型中`List<S>`被认为是`List<? extends T>`的子类型? |
36 |
| -这是片段的第三个变体: |
| 33 | +由于 `List<Integer>` 不被认为是 `List<Number>` 的子类型,因此在第二行而不是第三行检测到问题,并且在编译时检测到,而不是在运行时检测到。 |
| 34 | + |
| 35 | +通配符重新引入泛型的协变子类型,在当S是T的子类型时,这种类型中 `List<S>` 被认为是 `List<? extends T>` 的子类型? 这是片段的第三个变体: |
37 | 36 |
|
38 | 37 | ```java
|
39 | 38 | List<Integer> ints = Arrays.asList(1,2,3);
|
40 | 39 | List<? extends Number> nums = ints;
|
41 | 40 | nums.set(2, 3.14); // compile-time error
|
42 | 41 | assert ints.toString().equals("[1, 2, 3.14]"); // uh oh!
|
43 | 42 | ```
|
44 |
| -- 和数组一样,第三行是错误的,但是与数组相比,这个问题在编译时被检测到, |
45 |
| -而不是运行时。 该分配违反了“获取和放置原则”,因为您不能将值放入使用`extends`通配符声明的类型中。 |
| 43 | + |
| 44 | +和数组一样,第三行是错误的,但是与数组相比,这个问题在编译时被检测到,而不是运行时。 该分配违反了“获取和放置原则”,因为您不能将值放入使用 `extends` |
| 45 | +通配符声明的类型中。 |
46 | 46 |
|
47 |
| -- 通配符还引入了泛型的逆变分类,当`S`是`T`的超类型(而不是子类型)时, |
48 |
| -这种类型`List<S>`是被认为是`List<? super T>`一个子类型? 。 数组不支持逆分类。 |
49 |
| -例如,回想一下,方法`count`接受了一个类型`Collection<? super Integer>`的参数。 |
50 |
| -并填充整数。 因为`Java`不允许你编写`(?super Integer)[]`,所以没有与数组做同样的方法。 |
| 47 | +通配符还引入了泛型的逆变分类,当 `S` 是 `T` 的超类型(而不是子类型)时,这种类型 `List<S>` 是被认为是 `List<? super T>` 一个子类型? 。 数组不支持 |
| 48 | +逆分类。 例如,回想一下,方法 `count` 接受了一个类型 `Collection<? super Integer>` 的参数。 并填充整数。 因为 `Java` 不允许你编写 |
| 49 | +`(?super Integer)[]`,所以没有与数组做同样的方法。 |
51 | 50 |
|
52 |
| -- 在编译时而不是在运行时检测问题会带来两个优点,一个小问题和一个主要问题。 |
53 |
| -次要优点是它更有效率。 系统不需要在运行时随身携带一个元素类型的描述, |
54 |
| -而且每次执行一个数组赋值时,系统都不需要检查这个描述。 |
55 |
| -主要优点是编译器检测到一个常见的错误族。 这改善了程序生命周期的各个方面: |
56 |
| -编码,调试,测试和维护都变得更简单,更快速,而且更轻量级。 |
| 51 | +在编译时而不是在运行时检测问题会带来两个优点,一个小问题和一个主要问题。 次要优点是它更有效率。 系统不需要在运行时随身携带一个元素类型的描述,而且每 |
| 52 | +次执行一个数组赋值时,系统都不需要检查这个描述。 主要优点是编译器检测到一个常见的错误族。 这改善了程序生命周期的各个方面:编码,调试,测试和维护都变 |
| 53 | +得更简单,更快速,而且更轻量级。 |
57 | 54 |
|
58 |
| -- 除了错误之前被捕获的事实之外,还有许多其他原因可以将收集类更倾向于数组。 |
59 |
| -集合比数组更灵活。数组支持的唯一操作是获取或设置一个组件,并且该表示是固定的。 |
60 |
| -集合支持许多额外的操作,包括测试遏制,添加和删除元素,比较或合并两个集合, |
61 |
| -以及提取列表的子列表。集合可以是列表(其中的顺序是重要的,元素可以重复) |
62 |
| -或集合(顺序不重要,元素可能不重复),可以使用许多表示,包括数组,链表, |
63 |
| -树和散列表。最后,便利类Collections和Arrays的比较表明,集合提供了非数组提供的许多操作, |
64 |
| -包括旋转或打乱列表的操作,查找集合的最大值以及使集合不可修改或同步。 |
| 55 | +除了错误之前被捕获的事实之外,还有许多其他原因可以将收集类更倾向于数组。集合比数组更灵活。数组支持的唯一操作是获取或设置一个组件,并且该表示是固定 |
| 56 | +的。集合支持许多额外的操作,包括测试遏制,添加和删除元素,比较或合并两个集合,以及提取列表的子列表。集合可以是列表(其中的顺序是重要的,元素可以重 |
| 57 | +复)或集合(顺序不重要,元素可能不重复),可以使用许多表示,包括数组,链表,树和散列表。最后,便利类 `Collections` 和 `Arrays` 的比较表明,集合提供 |
| 58 | +了非数组提供的许多操作,包括旋转或打乱列表的操作,查找集合的最大值以及使集合不可修改或同步。 |
65 | 59 |
|
66 |
| -- 尽管如此,还是有少数情况下数组比数据集更受欢迎。 原始类型的数组更有效率, |
67 |
| -因为它们不涉及拳击; 并分配到这样的数组不需要检查数组存储异常, |
68 |
| -因为基元类型的数组没有子类型。 尽管检查了数组存储异常, |
69 |
| -即使是使用当前代编译器的引用类型数组也可能比集合类更有效, |
70 |
| -所以您可能希望在关键的内部循环中使用数组。 与往常一样, |
71 |
| -您应该测量性能来验证这样的设计,尤其是因为未来的编译器可能会专门优化收集类。 |
72 |
| -最后,在某些情况下,出于兼容性的原因,数组可能是优选。 |
| 60 | +尽管如此,还是有少数情况下数组比数据集更受欢迎。 原始类型的数组更有效率,因为它们不涉及拳击; 并分配到这样的数组不需要检查数组存储异常,因为基元类型的 |
| 61 | +数组没有子类型。 尽管检查了数组存储异常,即使是使用当前代编译器的引用类型数组也可能比集合类更有效,所以您可能希望在关键的内部循环中使用数组。 与往常 |
| 62 | +一样,您应该测量性能来验证这样的设计,尤其是因为未来的编译器可能会专门优化收集类。 最后,在某些情况下,出于兼容性的原因,数组可能是优选。 |
73 | 63 |
|
74 |
| -- 总而言之,最好在编译时检测错误,而不是在运行时检测错误,但是`Java`数组在运行时被强制检测到某些错误, |
75 |
| -因为决定做出数组子类型协变。 这是一个很好的决定? 在泛型出现之前,这是绝对必要的。 |
76 |
| -例如,看下面的方法,这些方法用于对任何数组进行排序或使用给定值填充数组: |
| 64 | +总而言之,最好在编译时检测错误,而不是在运行时检测错误,但是 `Java` 数组在运行时被强制检测到某些错误,因为决定做出数组子类型协变。 这是一个很好的决 |
| 65 | +定? 在泛型出现之前,这是绝对必要的。 例如,看下面的方法,这些方法用于对任何数组进行排序或使用给定值填充数组: |
77 | 66 |
|
78 | 67 | ```java
|
79 | 68 | public static void sort(Object[] a);
|
80 | 69 | public static void fill(Object[] a, Object val);
|
81 | 70 | ```
|
82 |
| -- 由于协变,这些方法可以用来排序或填充任何引用类型的数组。 没有协变性, |
83 |
| -没有泛型,就没有办法声明适用于所有类型的方法。 但是,现在我们已经有了泛型, |
84 |
| -协变阵列就不再需要了。 现在我们可以给这些方法以下签名,直接说明它们适用于所有类型: |
| 71 | + |
| 72 | +由于协变,这些方法可以用来排序或填充任何引用类型的数组。 没有协变性,没有泛型,就没有办法声明适用于所有类型的方法。 但是,现在我们已经有了泛型,协变 |
| 73 | +阵列就不再需要了。 现在我们可以给这些方法以下签名,直接说明它们适用于所有类型: |
85 | 74 |
|
86 | 75 | ```java
|
87 | 76 | public static <T> void sort(T[] a);
|
88 | 77 | public static <T> void fill(T[] a, T val);
|
89 | 78 | ```
|
90 |
| -- 从某种意义上讲,协变数组是早期`Java`版本中缺乏泛型的人为因素。 一旦你有泛型,协变数组可能是错误的设计选择, |
91 |
| -保留它们的唯一原因是向后兼容。 |
92 | 79 |
|
93 |
| -- 第6.4节 - 第6.8节讨论泛型和数组之间的不方便的交互。 出于多种目的, |
94 |
| -将数组视为一个已弃用的类型可能是明智的。我们回到6.9节的这一点。 |
| 80 | +从某种意义上讲,协变数组是早期 `Java` 版本中缺乏泛型的人为因素。 一旦你有泛型,协变数组可能是错误的设计选择,保留它们的唯一原因是向后兼容。 |
| 81 | + |
| 82 | +第 `6.4` 节 - 第 `6.8` 节讨论泛型和数组之间的不方便的交互。 出于多种目的,将数组视为一个已弃用的类型可能是明智的。我们回到 `6.9` 节的这一点。 |
95 | 83 |
|
96 |
| -《《《 [上一节](06_Wildcards_Versus_Type_Parameters.md) <br/> |
| 84 | +《《《 [下一节](06_Wildcards_Versus_Type_Parameters.md) <br/> |
97 | 85 | 《《《 [返回首页](../README.md)
|
0 commit comments