Skip to content

Commit afe58f4

Browse files
authored
Update 02_Instance_Tests_and_Casts.md
1 parent 00f828f commit afe58f4

File tree

1 file changed

+41
-136
lines changed

1 file changed

+41
-136
lines changed
Lines changed: 41 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
《《《 [返回首页](../README.md) <br/>
2+
《《《 [上一节](01_Reifiable_Types.md)
3+
14
## 实例测试和示例
25

36
实例测试和示例取决于运行时检查的类型,因此取决于具体化。 因此,针对不可修饰类型的实例测试会报告错误,并且对不可修饰类型进行强制转换通常会发出警告。
@@ -18,7 +21,8 @@
1821
}
1922
```
2023

21-
`equals` 方法接受 `Object` 类型的参数,检查对象是否为 `Integer` 类的实例,如果是,则将其转换为 `Integer` 并比较两个整数的值。 此代码可用,因为 `Integer` 是可重用类型:运行时可以使用检查对象是否为 `Integer` 实例所需的所有信息。
24+
`equals` 方法接受 `Object` 类型的参数,检查对象是否为 `Integer` 类的实例,如果是,则将其转换为 `Integer` 并比较两个整数的值。 此代码可用,因为
25+
`Integer` 是可重用类型:运行时可以使用检查对象是否为 `Integer` 实例所需的所有信息。
2226

2327
现在考虑一下如何在列表上定义相等性,就像 `java.util` 中的 `AbstractList` 类一样。 定义这个的自然但不正确的方式如下:
2428

@@ -42,9 +46,12 @@
4246
}
4347
```
4448

45-
同样,`equals` 方法接受 `Object` 类型的参数,检查对象是否为 `List<E>` 类型的实例,如果是,则将其转换为 `List<E>` 并比较两个列表中的相应元素。此代码不起作用,因为 `List<E>` 不是可重用类型:检查对象是否为 `List<E>`的实例所需的某些信息在运行时不可用。您可以测试一个对象是否实现接口 `List`,但不能测试其类型参数是否为 `E`。事实上,有关E的信息缺少双倍的数据,因为它不适用于接收方或 `equals` 方法的参数。
49+
同样,`equals` 方法接受 `Object` 类型的参数,检查对象是否为 `List<E>` 类型的实例,如果是,则将其转换为 `List<E>` 并比较两个列表中的相应元素。此代
50+
码不起作用,因为 `List<E>` 不是可重用类型:检查对象是否为 `List<E>`的实例所需的某些信息在运行时不可用。您可以测试一个对象是否实现接口 `List`,但不
51+
能测试其类型参数是否为 `E`。事实上,有关 `E` 的信息缺少双倍的数据,因为它不适用于接收方或 `equals` 方法的参数。
4652

47-
(即使这个代码有效,还有一个问题,列表上的相等约定没有提及类型,如果 `List<Integer>` 包含相同的值,那么它们可能与 `List<Object>` 相等。例如,`[1,2,3]` 应该与自身相等,无论它是否被视为整数列表或对象列表。)
53+
(即使这个代码有效,还有一个问题,列表上的相等约定没有提及类型,如果 `List<Integer>` 包含相同的值,那么它们可能与 `List<Object>` 相等。例如,
54+
`[1,2,3]` 应该与自身相等,无论它是否被视为整数列表或对象列表。)
4855

4956
编译上面的代码报告了两个问题,一个是实例测试的错误,另一个是未经检查的演员警告:
5057

@@ -62,7 +69,8 @@
6269
1 warning
6370
```
6471

65-
实例检查报告错误,因为没有可能的方法来测试给定对象是否属于类型 `List<E>`。 演员报告未经检查的警告; 它将执行转换,但它不能检查列表元素实际上是否为 `E` 类型。
72+
实例检查报告错误,因为没有可能的方法来测试给定对象是否属于类型 `List<E>`。 演员报告未经检查的警告; 它将执行转换,但它不能检查列表元素实际上是否为
73+
`E` 类型。
6674

6775
为了解决这个问题,我们用可调整类型 `List<?>` 替换了不可保留类型 `List<E>`。 这是一个更正的定义(再次,从实际来源稍微简化):
6876

@@ -87,11 +95,15 @@
8795
}
8896
```
8997

90-
除了更改实例测试和强制类型外,第二个迭代器的类型也从 `Iterator<E>` 更改为 `Iterator<?>`,第二个元素的类型从 `E` 更改为 `Object`。 代码类型检查,因为即使第二个迭代器的元素类型是未知的,它也保证它必须是 `Object` 的一个子类型,并且对 `equals` 的嵌套调用只需要它的第二个参数是一个对象。
98+
除了更改实例测试和强制类型外,第二个迭代器的类型也从 `Iterator<E>` 更改为 `Iterator<?>`,第二个元素的类型从 `E` 更改为 `Object`。 代码类型检查,
99+
因为即使第二个迭代器的元素类型是未知的,它也保证它必须是 `Object` 的一个子类型,并且对 `equals` 的嵌套调用只需要它的第二个参数是一个对象。
91100

92101
(这段代码正确地满足列表上的等式约定,现在如果一个 `List<Integer>` 包含相同的值,那么它们将等于一个 `List<Object>`。)
93102

94-
替代修复是可能的。 而不是通配符类型 `List<?>``Iterator<?>`,您可以使用原始类型 `List``Iterator`,这些类型也是可验证的。 我们建议使用无界通配符类型而不是原始类型,因为它们提供了更强的静态类型保证; 如果您使用原始类型,那么在使用无界通配符时只会将标记为警告的许多错误标记为警告。 此外,您可以将第一个迭代器的声明更改为 `Iterator<?>`,将第一个元素的声明更改为 `Object`,以使它们与第二个迭代器匹配,并且代码仍然会进行类型检查。 我们建议始终使用尽可能具体的类型声明; 这有助于编译器捕获更多错误并编译更高效的代码。
103+
替代修复是可能的。 而不是通配符类型 `List<?>``Iterator<?>`,您可以使用原始类型 `List``Iterator`,这些类型也是可验证的。 我们建议使用无界
104+
通配符类型而不是原始类型,因为它们提供了更强的静态类型保证; 如果您使用原始类型,那么在使用无界通配符时只会将标记为警告的许多错误标记为警告。 此外,您
105+
可以将第一个迭代器的声明更改为 `Iterator<?>`,将第一个元素的声明更改为 `Object`,以使它们与第二个迭代器匹配,并且代码仍然会进行类型检查。 我们建议
106+
始终使用尽可能具体的类型声明; 这有助于编译器捕获更多错误并编译更高效的代码。
95107

96108
**不可接受的转换**对不可转换类型的实例测试通常是错误的。 但是,在某些情况下,转换为不可赋予的类型是允许的。
97109

@@ -106,11 +118,14 @@
106118
}
107119
```
108120

109-
编译此代码将成功,不会出现错误或警告。 实例测试没有错误,因为 `List<?>` 是可重用的类型。 由于转换源的类型为 `Collection<T>`,转换不会报告警告,并且任何实现了接口 `List` 的类型的对象实际上都必须具有 `List<T>` 类型。
121+
编译此代码将成功,不会出现错误或警告。 实例测试没有错误,因为 `List<?>` 是可重用的类型。 由于转换源的类型为 `Collection<T>`,转换不会报告警告,并且
122+
任何实现了接口 `List` 的类型的对象实际上都必须具有 `List<T>` 类型。
110123

111-
**未经检查的强制转换**编译器很少能够确定如果强制转换为不可继承类型,则必须产生该类型的值。在其余情况下,对不可修饰的类型进行强制转换时将标记未经检查的警告,而针对不可修饰类型的实例测试始终会被捕获为错误。这是因为对于无法执行的实例测试而言,从来没有任何意义,但是可能存在无法检查的强制转换。
124+
**未经检查的强制转换**编译器很少能够确定如果强制转换为不可继承类型,则必须产生该类型的值。在其余情况下,对不可修饰的类型进行强制转换时将标记未经检查
125+
的警告,而针对不可修饰类型的实例测试始终会被捕获为错误。这是因为对于无法执行的实例测试而言,从来没有任何意义,但是可能存在无法检查的强制转换。
112126

113-
类型系统推断出程序的事实 - 例如,某个变量总是包含一个字符串列表。但是没有哪种类型的系统是完美的总会有一些程序员可以推论出来的事实,但是类型系统却没有。为了允许程序员在这种情况下找到解决方法,编译器在执行某些强制转换时发出警告而不是错误。
127+
类型系统推断出程序的事实 - 例如,某个变量总是包含一个字符串列表。但是没有哪种类型的系统是完美的总会有一些程序员可以推论出来的事实,但是类型系统却没
128+
有。为了允许程序员在这种情况下找到解决方法,编译器在执行某些强制转换时发出警告而不是错误。
114129

115130
例如,下面是将对象列表提升为字符串列表的代码,如果对象列表仅包含字符串,则会抛出类转换异常:
116131

@@ -138,7 +153,8 @@
138153

139154
如果任何对象不是字符串,该方法会在对象列表上抛出循环并抛出类抛出异常。 因此,当方法的最后一行到达时,将对象列表转换为字符串列表是安全的。
140155

141-
但编译器无法推断出这一点,因此程序员必须使用未经检查的 `cast`。将对象列表转换为字符串列表是非法的,因此必须分两步进行。 首先,将对象列表转换为通配符类型列表; 这个演员是安全的。 其次,将通配符类型列表转换为字符串列表; 此演员阵容是允许的,但会产生未经检查的警告:
156+
但编译器无法推断出这一点,因此程序员必须使用未经检查的 `cast`。将对象列表转换为字符串列表是非法的,因此必须分两步进行。 首先,将对象列表转换为通配符
157+
类型列表; 这个演员是安全的。 其次,将通配符类型列表转换为字符串列表; 此演员阵容是允许的,但会产生未经检查的警告:
142158

143159
```java
144160
% javac -Xlint:unchecked Promote.java
@@ -150,137 +166,26 @@
150166
1 warning
151167
```
152168

153-
测试代码将该方法应用于两个列表,一个仅包含字符串(因此成功),另一个包含整数(因此引发异常)。在第一个断言中,为了比较对象列表和字符串列表,我们必须首先将两个类型转换为类型 `List<?>`(这个转换是安全的),因为尝试比较列表中的对象列表和字符串列表会产生一个类型错误。
169+
测试代码将该方法应用于两个列表,一个仅包含字符串(因此成功),另一个包含整数(因此引发异常)。在第一个断言中,为了比较对象列表和字符串列表,我们必须
170+
首先将两个类型转换为类型 `List<?>`(这个转换是安全的),因为尝试比较列表中的对象列表和字符串列表会产生一个类型错误。
154171

155-
如果原始列表仅包含字符串,则可以使用完全相同的技术将原始列表提升为字符串列表。这种技术对于将遗留代码和泛型代码进行拟合很重要,也是使用擦除来实现泛型的主要原因之一。相关技术在第 `8.1` 节中讨论。
172+
如果原始列表仅包含字符串,则可以使用完全相同的技术将原始列表提升为字符串列表。这种技术对于将遗留代码和泛型代码进行拟合很重要,也是使用擦除来实现泛型
173+
的主要原因之一。相关技术在第 `8.1` 节中讨论。
156174

157175
`5.4.1` 节中,我们需要对元素类型(`E`)进行未经检查的转换,以使传统add方法返回的值的类型与其泛型相匹配签名。
158176

159-
您应该尽量减少代码中未经检查的强制转换次数,但有时候,如上所述,它们是无法避免的。在本书中,我们遵循这样一个惯例,即我们总是将评论放在包含演员阵容的一行上,以表明这是一种有意的解决方法,而不是无意中的滑动;我们建议你也这样做。将注释放在与转换相同的行上非常重要,以便在扫描编译器发出的警告时很容易确认每行都包含注释。如果没有,那么你应该把警告等同于一个错误!
160-
161-
如果某个方法故意包含未经检查的强制转换,则可能希望在注释之前添加注释 `@SuppressWarnings(“unchecked”)` 以避免虚假警告。 我们在 `5.4.1` 节看到了这种技术的应用。
162-
163-
作为使用未经检查的强制转换的另一个例子,在第 `6.5` 节中,我们将看到使用类型为 `Object[]` 的未经检查强制类型转换为 `T []` 的代码。 由于对象数组的创建方式,事实上,它确保数组始终具有正确的类型。
164-
165-
`C` 中(和它的后代 `C++`)未经检查的强制转换比在 `Java` 中未经检查的强制转换危险得多。 与 `C` 不同,即使存在未经检查的强制转换,`Java` 运行时也能保证重要的安全属性; 例如,绝不允许使用数组边界之外的索引访问数组。 尽管如此,在 `Java` 中未经检查的强制转换是一种解决方法,应谨慎使用。
166-
167-
168-
169-
170-
171-
172-
173-
174-
175-
176-
177-
178-
179-
180-
181-
182-
183-
184-
185-
186-
187-
188-
189-
190-
191-
192-
193-
194-
195-
196-
197-
198-
199-
200-
201-
202-
203-
204-
205-
206-
207-
208-
209-
210-
211-
212-
213-
214-
215-
216-
217-
218-
219-
220-
221-
222-
223-
224-
225-
226-
227-
228-
229-
230-
231-
232-
233-
234-
235-
236-
237-
238-
239-
240-
241-
242-
243-
244-
245-
246-
247-
248-
249-
250-
251-
252-
253-
254-
255-
256-
257-
258-
259-
260-
261-
262-
263-
264-
265-
266-
267-
268-
269-
270-
271-
272-
273-
274-
275-
276-
277-
278-
279-
280-
281-
282-
177+
您应该尽量减少代码中未经检查的强制转换次数,但有时候,如上所述,它们是无法避免的。在本书中,我们遵循这样一个惯例,即我们总是将评论放在包含演员阵容的
178+
一行上,以表明这是一种有意的解决方法,而不是无意中的滑动;我们建议你也这样做。将注释放在与转换相同的行上非常重要,以便在扫描编译器发出的警告时很容易确
179+
认每行都包含注释。如果没有,那么你应该把警告等同于一个错误!
283180

181+
如果某个方法故意包含未经检查的强制转换,则可能希望在注释之前添加注释 `@SuppressWarnings(“unchecked”)` 以避免虚假警告。 我们在 `5.4.1` 节看到了这
182+
种技术的应用。
284183

184+
作为使用未经检查的强制转换的另一个例子,在第 `6.5` 节中,我们将看到使用类型为 `Object[]` 的未经检查强制类型转换为 `T []` 的代码。 由于对象数组的创
185+
建方式,事实上,它确保数组始终具有正确的类型。
285186

187+
`C` 中(和它的后代 `C++`)未经检查的强制转换比在 `Java` 中未经检查的强制转换危险得多。 与 `C` 不同,即使存在未经检查的强制转换,`Java` 运行时也
188+
能保证重要的安全属性; 例如,绝不允许使用数组边界之外的索引访问数组。 尽管如此,在 `Java` 中未经检查的强制转换是一种解决方法,应谨慎使用。
286189

190+
《《《 [下一节](03_Exception_Handling.md) <br/>
191+
《《《 [返回首页](../README.md)

0 commit comments

Comments
 (0)