|
8 | 8 | 作为一个例子,考虑使用实例测试并以书面形式强制转换。 下面是 `java.lang` 中类 `Integer` 定义的一个片段(从实际源代码稍微简化了一下):
|
9 | 9 |
|
10 | 10 | ```java
|
11 |
| - public class Integer extends Number { |
12 |
| - private final int value; |
13 |
| - public Integer(int value) { this.value=value; } |
14 |
| - public int intValue() { return value; } |
15 |
| - public boolean equals(Object o) { |
16 |
| - if (o instanceof Integer) { |
17 |
| - return value == ((Integer)o).intValue(); |
18 |
| - } else return false; |
19 |
| - } |
20 |
| - ... |
21 |
| - } |
| 11 | +public class Integer extends Number { |
| 12 | + private final int value; |
| 13 | + public Integer(int value) { |
| 14 | + this.value=value; |
| 15 | + } |
| 16 | + public int intValue() { |
| 17 | + return value; |
| 18 | + } |
| 19 | + public boolean equals(Object o) { |
| 20 | + if (o instanceof Integer) { |
| 21 | + return value == ((Integer)o).intValue(); |
| 22 | + } else |
| 23 | + return false; |
| 24 | + } |
| 25 | + ... |
| 26 | + } |
22 | 27 | ```
|
23 | 28 |
|
24 | 29 | `equals` 方法接受 `Object` 类型的参数,检查对象是否为 `Integer` 类的实例,如果是,则将其转换为 `Integer` 并比较两个整数的值。 此代码可用,因为
|
|
27 | 32 | 现在考虑一下如何在列表上定义相等性,就像 `java.util` 中的 `AbstractList` 类一样。 定义这个的自然但不正确的方式如下:
|
28 | 33 |
|
29 | 34 | ```java
|
30 |
| - import java.util.*; |
31 |
| - public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> { |
32 |
| - public boolean equals(Object o) { |
33 |
| - if (o instanceof List<E>) { // compile-time error |
34 |
| - Iterator<E> it1 = iterator(); |
35 |
| - Iterator<E> it2 = ((List<E>)o).iterator(); // unchecked cast |
36 |
| - while (it1.hasNext() && it2.hasNext()) { |
37 |
| - E e1 = it1.next(); |
38 |
| - E e2 = it2.next(); |
39 |
| - if (!(e1 == null ? e2 == null : e1.equals(e2))) |
40 |
| - return false; |
41 |
| - } |
42 |
| - return !it1.hasNext() && !it2.hasNext(); |
43 |
| - } else return false; |
44 |
| - } |
45 |
| - ... |
46 |
| - } |
| 35 | +import java.util.*; |
| 36 | +public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> { |
| 37 | + public boolean equals(Object o) { |
| 38 | + if (o instanceof List<E>) { // compile-time error |
| 39 | + Iterator<E> it1 = iterator(); |
| 40 | + Iterator<E> it2 = ((List<E>)o).iterator(); // unchecked cast |
| 41 | + while (it1.hasNext() && it2.hasNext()) { |
| 42 | + E e1 = it1.next(); |
| 43 | + E e2 = it2.next(); |
| 44 | + if (!(e1 == null ? e2 == null : e1.equals(e2))) |
| 45 | + return false; |
| 46 | + } |
| 47 | + return !it1.hasNext() && !it2.hasNext(); |
| 48 | + } else |
| 49 | + return false; |
| 50 | + } |
| 51 | + ... |
| 52 | + } |
47 | 53 | ```
|
48 | 54 |
|
49 | 55 | 同样,`equals` 方法接受 `Object` 类型的参数,检查对象是否为 `List<E>` 类型的实例,如果是,则将其转换为 `List<E>` 并比较两个列表中的相应元素。此代
|
|
56 | 62 | 编译上面的代码报告了两个问题,一个是实例测试的错误,另一个是未经检查的演员警告:
|
57 | 63 |
|
58 | 64 | ```java
|
59 |
| - % javac -Xlint:unchecked AbstractList.java |
60 |
| - AbstractList.java:6: illegal generic type for instanceof |
61 |
| - if (!(o instanceof List<E>)) return false; // compile-time error |
62 |
| - ^ |
63 |
| - AbstractList.java:8: warning: [unchecked] unchecked cast |
64 |
| - found : java.lang.Object |
65 |
| - required: List<E> |
66 |
| - Iterator<E> it2 = ((List<E>)o).iterator(); // unchecked cast |
67 |
| - ^ |
68 |
| - 1 error |
69 |
| - 1 warning |
| 65 | + % javac -Xlint:unchecked AbstractList.java |
| 66 | + AbstractList.java:6: illegal generic type for instanceof |
| 67 | + if (!(o instanceof List<E>)) return false; // compile-time error |
| 68 | + ^ |
| 69 | + AbstractList.java:8: warning: [unchecked] unchecked cast |
| 70 | + found : java.lang.Object |
| 71 | + required: List<E> |
| 72 | + Iterator<E> it2 = ((List<E>)o).iterator(); // unchecked cast |
| 73 | + ^ |
| 74 | + 1 error |
| 75 | + 1 warning |
70 | 76 | ```
|
71 | 77 |
|
72 | 78 | 实例检查报告错误,因为没有可能的方法来测试给定对象是否属于类型 `List<E>`。 演员报告未经检查的警告; 它将执行转换,但它不能检查列表元素实际上是否为
|
|
75 | 81 | 为了解决这个问题,我们用可调整类型 `List<?>` 替换了不可保留类型 `List<E>`。 这是一个更正的定义(再次,从实际来源稍微简化):
|
76 | 82 |
|
77 | 83 | ```java
|
78 |
| - import java.util.*; |
79 |
| - public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> { |
80 |
| - public boolean equals(Object o) { |
81 |
| - if (o instanceof List<?>) { |
82 |
| - Iterator<E> it1 = iterator(); |
83 |
| - Iterator<?> it2 = ((List<?>)o).iterator(); |
84 |
| - while (it1.hasNext() && it2.hasNext()) { |
85 |
| - E e1 = it1.next(); |
86 |
| - Object e2 = it2.next(); |
87 |
| - if (!(e1 == null ? e2 == null : e1.equals(e2))) |
88 |
| - return false; |
89 |
| - } |
90 |
| - return !it1.hasNext() && !it2.hasNext(); |
91 |
| - } else |
92 |
| - return false; |
93 |
| - } |
94 |
| - ... |
95 |
| - } |
| 84 | +import java.util.*; |
| 85 | +public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> { |
| 86 | + public boolean equals(Object o) { |
| 87 | + if (o instanceof List<?>) { |
| 88 | + Iterator<E> it1 = iterator(); |
| 89 | + Iterator<?> it2 = ((List<?>)o).iterator(); |
| 90 | + while (it1.hasNext() && it2.hasNext()) { |
| 91 | + E e1 = it1.next(); |
| 92 | + Object e2 = it2.next(); |
| 93 | + if (!(e1 == null ? e2 == null : e1.equals(e2))) |
| 94 | + return false; |
| 95 | + } |
| 96 | + return !it1.hasNext() && !it2.hasNext(); |
| 97 | + } else |
| 98 | + return false; |
| 99 | + } |
| 100 | + ... |
| 101 | + } |
96 | 102 | ```
|
97 | 103 |
|
98 | 104 | 除了更改实例测试和强制类型外,第二个迭代器的类型也从 `Iterator<E>` 更改为 `Iterator<?>`,第二个元素的类型从 `E` 更改为 `Object`。 代码类型检查,
|
|
110 | 116 | 例如,以下方法将集合转换为列表:
|
111 | 117 |
|
112 | 118 | ```java
|
113 |
| - public static <T> List<T> asList(Collection<T> c) throws InvalidArgumentException { |
114 |
| - if (c instanceof List<?>) { |
115 |
| - return (List<T>)c; |
116 |
| - } else |
117 |
| - throw new InvalidArgumentException("Argument not a list"); |
118 |
| - } |
| 119 | +public static <T> List<T> asList(Collection<T> c) throws InvalidArgumentException { |
| 120 | + if (c instanceof List<?>) { |
| 121 | + return (List<T>)c; |
| 122 | + } else |
| 123 | + throw new InvalidArgumentException("Argument not a list"); |
| 124 | + } |
119 | 125 | ```
|
120 | 126 |
|
121 | 127 | 编译此代码将成功,不会出现错误或警告。 实例测试没有错误,因为 `List<?>` 是可重用的类型。 由于转换源的类型为 `Collection<T>`,转换不会报告警告,并且
|
|
130 | 136 | 例如,下面是将对象列表提升为字符串列表的代码,如果对象列表仅包含字符串,则会抛出类转换异常:
|
131 | 137 |
|
132 | 138 | ```java
|
133 |
| - class Promote { |
134 |
| - public static List<String> promote(List<Object> objs) { |
135 |
| - for (Object o : objs) |
136 |
| - if (!(o instanceof String)) |
137 |
| - throw new ClassCastException(); |
138 |
| - return (List<String>)(List<?>)objs; // unchecked cast |
139 |
| - } |
140 |
| - public static void main(String[] args) { |
141 |
| - List<Object> objs1 = Arrays.<Object>asList("one","two"); |
142 |
| - List<Object> objs2 = Arrays.<Object>asList(1,"two"); |
143 |
| - List<String> strs1 = promote(objs1); |
144 |
| - assert (List<?>)strs1 == (List<?>)objs1; |
145 |
| - boolean caught = false; |
146 |
| - try { |
147 |
| - List<String> strs2 = promote(objs2); |
148 |
| - } catch (ClassCastException e) { caught = true; } |
149 |
| - assert caught; |
150 |
| - } |
151 |
| - } |
| 139 | +class Promote { |
| 140 | + public static List<String> promote(List<Object> objs) { |
| 141 | + for (Object o : objs) |
| 142 | + if (!(o instanceof String)) |
| 143 | + throw new ClassCastException(); |
| 144 | + return (List<String>)(List<?>)objs; // unchecked cast |
| 145 | + } |
| 146 | + public static void main(String[] args) { |
| 147 | + List<Object> objs1 = Arrays.<Object>asList("one","two"); |
| 148 | + List<Object> objs2 = Arrays.<Object>asList(1,"two"); |
| 149 | + List<String> strs1 = promote(objs1); |
| 150 | + assert (List<?>)strs1 == (List<?>)objs1; |
| 151 | + boolean caught = false; |
| 152 | + try { |
| 153 | + List<String> strs2 = promote(objs2); |
| 154 | + } catch (ClassCastException e) { |
| 155 | + caught = true; |
| 156 | + } |
| 157 | + assert caught; |
| 158 | + } |
| 159 | + } |
152 | 160 | ```
|
153 | 161 |
|
154 | 162 | 如果任何对象不是字符串,该方法会在对象列表上抛出循环并抛出类抛出异常。 因此,当方法的最后一行到达时,将对象列表转换为字符串列表是安全的。
|
|
157 | 165 | 类型列表; 这个演员是安全的。 其次,将通配符类型列表转换为字符串列表; 此演员阵容是允许的,但会产生未经检查的警告:
|
158 | 166 |
|
159 | 167 | ```java
|
160 |
| - % javac -Xlint:unchecked Promote.java |
161 |
| - Promote.java:7: warning: [unchecked] unchecked cast |
162 |
| - found : java.util.List |
163 |
| - required: java.util.List<java.lang.String> |
164 |
| - return (List<String>)(List<?>)objs; // unchecked cast |
165 |
| - ^ |
166 |
| - 1 warning |
| 168 | + % javac -Xlint:unchecked Promote.java |
| 169 | + Promote.java:7: warning: [unchecked] unchecked cast |
| 170 | + found : java.util.List |
| 171 | + required: java.util.List<java.lang.String> |
| 172 | + return (List<String>)(List<?>)objs; // unchecked cast |
| 173 | + ^ |
| 174 | + 1 warning |
167 | 175 | ```
|
168 | 176 |
|
169 | 177 | 测试代码将该方法应用于两个列表,一个仅包含字符串(因此成功),另一个包含整数(因此引发异常)。在第一个断言中,为了比较对象列表和字符串列表,我们必须
|
|
0 commit comments