Skip to content

Commit 81d25cd

Browse files
committed
获取和放置原则
1 parent 24f4829 commit 81d25cd

4 files changed

+175
-2
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
* [子类型化和替代原则](ch02/01_Subtyping_and_the_Substitution_Principle.md#子类型化和替代原则)
2929
* [通配符和继承](ch02/02_Wildcards_with_extends.md#通配符和继承)
3030
* [通配符和超类](ch02/03_Wildcards_with_super.md#通配符和超类)
31+
* [获取和放置原则](ch02/04_The_Get_and_Put_Principle.md#获取和放置原则)
3132

3233

3334
---

SUMMARY.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@
1818
* [第二章(子类型化和通配符)](ch02/00_Subtyping_and_Wildcards.md#第二章(子类型化和通配符))
1919
* [子类型化和替代原则](ch02/01_Subtyping_and_the_Substitution_Principle.md#子类型化和替代原则)
2020
* [通配符和继承](ch02/02_Wildcards_with_extends.md#通配符和继承)
21-
* [通配符和超类](ch02/03_Wildcards_with_super.md#通配符和超类)
21+
* [通配符和超类](ch02/03_Wildcards_with_super.md#通配符和超类)
22+
* [获取和放置原则](ch02/04_The_Get_and_Put_Principle.md#获取和放置原则)

ch02/03_Wildcards_with_super.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
public static <T> void copy(List<? super T> dst, List<T> src)
4848
public static <T> void copy(List<? super T> dst, List<? extends T> src)
4949
```
50-
- 第一种限制性太强,因为只有当目的地和来源具有完全相同的类型时才允许呼叫
50+
- 第一种限制性太强,因为只有当目的地和来源具有完全相同的类型时才允许调用
5151
其余三个对于使用隐式类型参数的调用是等效的,但是对于显式类型参数不同。
5252
对于上面的示例调用,第二个签名仅在类型参数为`Object`时起作用,第三个签名仅在类型参数为`Integer`时起作用,
5353
最后一个签名对所有三个类型参数起作用(如我们所见),即`Object``Number``Integer`

ch02/04_The_Get_and_Put_Principle.md

+171
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
《《《 [返回首页](../README.md)
2+
3+
### 获取和放置原则
4+
5+
- 尽可能插入通配符可能是一个好习惯,但是如何决定使用哪个通配符? 你应该在哪里使用`extends`,你应该在哪里使用`super`
6+
在哪里不适合使用通配符?
7+
8+
- 幸运的是,一个简单的原则决定哪一个是合适的。
9+
10+
> 获取和放置原则:当您只将值从结构中取出时,使用扩展通配符,
11+
当您只将值放入结构中时使用超级通配符,并且在双方都得到并放置时不使用通配符。
12+
13+
- 我们已经在复制方法的签名中看到了这个原理:
14+
15+
```java
16+
public static <T> void copy(List<? super T> dest, List<? extends T> src)
17+
```
18+
- 该方法从源`src`中获取值,因此使用扩展通配符声明值,并将值放入目标`dst`中,
19+
因此使用超级通配符声明值。
20+
21+
- 无论何时使用迭代器,都会从结构中获取值,因此请使用扩展通配符。 这是一个需要一个数字集合的方法,
22+
每个转换为一个双精度求和:
23+
24+
```java
25+
public static double sum(Collection<? extends Number> nums) {
26+
double s = 0.0;
27+
for (Number num : nums) s += num.doubleValue();
28+
return s;
29+
}
30+
```
31+
32+
- 由于这个使用`extends`,所有以下的调用是合法的:
33+
34+
```java
35+
List<Integer> ints = Arrays.asList(1,2,3);
36+
assert sum(ints) == 6.0;
37+
List<Double> doubles = Arrays.asList(2.78,3.14);
38+
assert sum(doubles) == 5.92;
39+
List<Number> nums = Arrays.<Number>asList(1,2,2.78,3.14);
40+
assert sum(nums) == 8.92;
41+
```
42+
43+
- 如果不使用`extends`,前两个调用将不合法
44+
45+
- 每当你使用add方法时,你把值放到一个结构中,所以使用`super`通配符。
46+
这是一个采用数字和整数n的集合的方法将从零开始的前n个整数放入集合中:
47+
48+
```java
49+
public static void count(Collection<? super Integer> ints, int n) {
50+
for (int i = 0; i < n; i++) ints.add(i);
51+
}
52+
```
53+
54+
- 由于这使用`super`,以下所有调用都是合法的:
55+
56+
```java
57+
List<Integer> ints = new ArrayList<Integer>();
58+
count(ints, 5);
59+
assert ints.toString().equals("[0, 1, 2, 3, 4]");
60+
List<Number> nums = new ArrayList<Number>();
61+
count(nums, 5); nums.add(5.0);
62+
assert nums.toString().equals("[0, 1, 2, 3, 4, 5.0]");
63+
List<Object> objs = new ArrayList<Object>();
64+
count(objs, 5); objs.add("five");
65+
assert objs.toString().equals("[0, 1, 2, 3, 4, five]");
66+
```
67+
68+
- 如果`super`不被使用,最后两个调用将是不合法的。
69+
- 无论何时您将值放入并从同一结构中获取值,都不应使用通配符。
70+
71+
```java
72+
public static double sumCount(Collection<Number> nums, int n) {
73+
count(nums, n);
74+
return sum(nums);
75+
}
76+
```
77+
78+
- 集合被传递给`sum``count`,所以它的元素类型都必须继承`Number`(按总数要求),
79+
`Integer`的超类(按计数要求)。 唯一满足这两个约束的两个类是·Number``Integer`,
80+
我们选择了第一个。 以下是一个调用示例:
81+
82+
```java
83+
List<Number> nums = new ArrayList<Number>();
84+
double sum = sumCount(nums,5);
85+
assert sum == 10;
86+
```
87+
- 由于没有通配符,参数必须是`Number`的集合。
88+
- 如果您不喜欢在`Number``Integer`之间进行选择,那么您可能会想到,
89+
如果`Java`允许您使用`extends``super`编写通配符,则不需要选择。
90+
例如,我们可以写下以下内容:
91+
92+
```java
93+
double sumCount(Collection<? extends Number super Integer> coll, int n)
94+
// 这在java里面是非法的
95+
```
96+
97+
- 然后我们可以在一个数字集合或一个整数集合上调用`sumCount`。 但Java不允许这样做。
98+
打乱它的唯一原因是简单,可以想象`Java`在将来可能会支持这种表示法。
99+
但是,现在,如果你想同时获取和放置不要使用通配符。
100+
101+
- 获取和放置原则也是相反的。 如果扩展通配符存在,几乎所有的都是获取但不是放置类型的值;
102+
如果存在一个超级通配符,几乎所有你能够做的就是放置,但是不能获得这种类型的值。
103+
104+
- 例如,考虑下面的代码片段,它使用一个用扩展通配符声明的列表:
105+
106+
```java
107+
List<Integer> ints = new ArrayList<Integer>();
108+
ints.add(1);
109+
ints.add(2);
110+
List<? extends Number> nums = ints;
111+
double dbl = sum(nums); // ok
112+
nums.add(3.14); // compile-time error
113+
```
114+
- 调用它求和是好的,因为它从列表中获取值,但调用`add`的不是,因为它将一个值放入列表中。
115+
这也是一样,因为否则我们可以添加双整数列表!
116+
117+
- 相反,考虑下面的代码片段,它使用一个超级通配符声明的列表:
118+
119+
```java
120+
List<Object> objs = new ArrayList<Object>();
121+
objs.add(1);
122+
objs.add("two");
123+
List<? super Integer> ints = objs;
124+
ints.add(3); // ok
125+
double dbl = sum(ints); // 编译报错
126+
```
127+
- 现在调用`add`是正常的,因为它将一个值放入列表中,但是对`sum`的调用不是,
128+
因为它从列表中获取值。 这也是一样,因为包含一个字符串的列表的总和是没有意义的!
129+
130+
- 例外证明了这个规则,而且每个规则都有一个例外。 你不能把任何东西放到用扩展通配符声明的类型中
131+
-- 除了属于每个引用类型的值为`null`
132+
133+
```java
134+
List<Integer> ints = new ArrayList<Integer>();
135+
ints.add(1);
136+
ints.add(2);
137+
List<? extends Number> nums = ints;
138+
nums.add(null); // ok
139+
assert nums.toString().equals("[1, 2, null]");
140+
```
141+
- 同样,你也不能从使用超级通配符声明的类型中获取任何东西 - 除了`Object`类型的值,
142+
它是每个引用类型的超类型:
143+
144+
```java
145+
List<Object> objs = Arrays.<Object>asList(1,"two");
146+
List<? super Integer> ints = objs;
147+
String str = "";
148+
for (Object obj : ints) str += obj.toString();
149+
assert str.equals("1two");
150+
```
151+
- 你可能会觉得有帮助的想法? 将`T`扩展为包含每个类型的一个区间,该区间由下面的`null`类型
152+
和上面的`T`(其中`null`的类型是每个引用类型的子类型)限定。 同样,你可能会想到?
153+
`super T`包含每个类型在由`T`和由上面的`Object`限定的区间中。
154+
155+
- 认为扩展通配符确保不变性是诱人的,但事实并非如此。正如我们前面看到的,
156+
给定一个列表`List <? extends Number>`,你仍然可以添加`null`值列表。
157+
您也可以删除列表元素(使用`remove``removeAll``retainAll`)或对列表进行置换
158+
(在便捷类集合中使用交换,排序或随机操作;参见第17.1.1节)。 如果要确保列表不能更改,
159+
请使用类`Collections`中的`unmodifiableList`方法; 其他集合类也有类似的方法(见17.3.2节)。
160+
如果你想确保列表元素不能被改变,可以考虑按照`Joshua Bloch`在他的书第四章有效Java(Addison-Wesley)
161+
(“最小化可变性”/“Favour不变性”)中给出的类不可变的规则。; 例如,在第二部分中,
162+
第12.1节中的类`CodingTask``PhoneTask`是不可变的,就像第13.2节中的类`PriorityTask`一样。
163+
164+
- 因为`String``final`的,并且可以没有子类型,所以您可能期望`List <String>``List <? extends String>`是相同的类型。
165+
但实际上前者是后者的一个子类型,但不是同样的类型,正如应用我们的原则所能看到的那样。
166+
替代原则告诉我们这是一个子类型,因为传递前者类型的值是可以的。 获取和放置原则告诉我们它不是相同的类型,
167+
因为我们可以添加一个字符串到前一个类型的值而不是后一个。
168+
169+
170+
171+
《《《 [返回首页](../README.md)

0 commit comments

Comments
 (0)