Skip to content

Commit f58a052

Browse files
committed
ADD
1 parent 888aeb8 commit f58a052

4 files changed

+203
-3
lines changed

ch02/03_Wildcards_with_super.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,5 @@
5454
最后一个签名对所有三个类型参数起作用(如我们所见),即`Object``Number``Integer`
5555
在签名中始终使用通配符,因为这样可以实现最广泛的调用。
5656

57-
57+
《《《 [下一节](04_The_Get_and_Put_Principle.md) <br/>
5858
《《《 [返回首页](../README.md)

ch02/04_The_Get_and_Put_Principle.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
《《《 [返回首页](../README.md)
1+
《《《 [返回首页](../README.md) <br/>
2+
《《《 [上一节](03_Wildcards_with_super.md)
23

34
### 获取和放置原则
45

@@ -167,5 +168,5 @@
167168
因为我们可以添加一个字符串到前一个类型的值而不是后一个。
168169

169170

170-
171+
《《《 [上一节](05_Arrays.md) <br/>
171172
《《《 [返回首页](../README.md)

ch02/05_Arrays.md

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

0 commit comments

Comments
 (0)