From dbaa3e4c0d68abc00899a481eaa7cfe7cd3541ca Mon Sep 17 00:00:00 2001 From: maskleo Date: Sat, 14 Apr 2018 00:01:43 +0800 Subject: [PATCH 01/21] Update 00_Introduction.md --- ch01/00_Introduction.md | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/ch01/00_Introduction.md b/ch01/00_Introduction.md index 709a8001..0d5e94b0 100644 --- a/ch01/00_Introduction.md +++ b/ch01/00_Introduction.md @@ -1,7 +1,6 @@ 《《《 [返回首页](../README.md)
《《《 [上一节](../Preface.md) - ## 第一章(简介)   `Java` 最新版本中现在对泛型和集合与许多其他新功能有良好的支持,包括装箱和拆箱,新的循环形式,以及接受可变数量参数的函数。我们从一个例子开始说明了这 @@ -10,10 +9,12 @@ 因此作为我们的座右铭,让我们做一些简单求和:把三个数字一个列表并将它们加在一起。 下面是如何在 `Java` 中使用泛型:   ```java - List ints = Arrays.asList(1,2,3); - int s = 0; - for (int n : ints) { s += n; } - assert s == 6; + List ints = Arrays.asList(1,2,3); + int s = 0; + for (int n : ints) { + s += n; + } + assert s == 6; ```   不需要太多的解释你就可以读懂代码,但是让我们来看看关键特征。接口列表和类数组是集合框架的一部分(都可以在 `java.util` 包中找到)。类型 `List` 现在是 @@ -25,15 +26,15 @@ 下面是在泛型之前 `Java` 中相同作用的代码: ```java - List ints = Arrays.asList( new Integer[] { - new Integer(1), new Integer(2), new Integer(3) - } ); - int s = 0; - for (Iterator it = ints.iterator(); it.hasNext(); ) { + List ints = Arrays.asList( new Integer[] { + new Integer(1), new Integer(2), new Integer(3) + } ); + int s = 0; + for (Iterator it = ints.iterator(); it.hasNext(); ) { int n = ((Integer)it.next()).intValue(); s += n; - } - assert s == 6; + } + assert s == 6; ```   阅读这段代码并不是那么容易。 没有泛型,就没有办法指出类型声明你打算在列表中存储什么样的元素,所以而不是写 `List`,你写 `List`。  现在是 @@ -44,10 +45,12 @@ 顺便说一句,下面是如何在泛型之前用 `Java` 中的数组做同样的事情: ```java - int[] ints = new int[] { 1,2,3 }; - int s = 0; - for (int i = 0; i < ints.length; i++) { s += ints[i]; } - assert s == 6; + int[] ints = new int[] { 1,2,3 }; + int s = 0; + for (int i = 0; i < ints.length; i++) { + s += ints[i]; + } + assert s == 6; ``` 这比使用泛型和集合的相应代码略长,可以说是不太可读,而且肯定不够灵活。 集合让你轻松增大或缩小集合的大小,或在切换到适当的不同的表示形式时,如链表或散 From 646a77564d5de73e704a73b7d5e75b2d7d744630 Mon Sep 17 00:00:00 2001 From: maskleo Date: Sun, 15 Apr 2018 17:58:35 +0800 Subject: [PATCH 02/21] Update 03_Nested_Classes.md --- ch04/03_Nested_Classes.md | 1 - 1 file changed, 1 deletion(-) diff --git a/ch04/03_Nested_Classes.md b/ch04/03_Nested_Classes.md index 634d9fa1..19038b35 100644 --- a/ch04/03_Nested_Classes.md +++ b/ch04/03_Nested_Classes.md @@ -100,4 +100,3 @@ 《《《 [下一节](04_How_Erasure_Works.md)
《《《 [返回首页](../README.md) - From 9b089672d72cebe609f689b75f40c76e9eb23258 Mon Sep 17 00:00:00 2001 From: maskleo Date: Sun, 15 Apr 2018 18:00:16 +0800 Subject: [PATCH 03/21] Update 04_How_Erasure_Works.md --- ch04/04_How_Erasure_Works.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ch04/04_How_Erasure_Works.md b/ch04/04_How_Erasure_Works.md index 24998fae..03fe1809 100644 --- a/ch04/04_How_Erasure_Works.md +++ b/ch04/04_How_Erasure_Works.md @@ -1,3 +1,6 @@ +《《《 [返回首页](../README.md)
+《《《 [上一节](03_Nested_Classes.md) + ## 擦除的工作原理 擦除类型的定义如下:从参数化类型中删除所有类型参数,并用删除它的边界来替换任何类型变量,或者如果它没有边界,则使用 `Object`;或者如果它具有最左边界 @@ -99,4 +102,5 @@ 如果这得到支持,通常需要对桥接方法进行复杂而混乱的定义(参见第 `3.7` 节)。 到目前为止,最简单和最容易理解的选择是禁止这种情况。 - +《《《 [下一节](../ch05/00_Evolution_Not_Revolution.md)
+《《《 [返回首页](../README.md) From 6d79481757a1ce351ad49ac6b3abfb530cba0b52 Mon Sep 17 00:00:00 2001 From: maskleo Date: Sun, 15 Apr 2018 18:01:57 +0800 Subject: [PATCH 04/21] Update 00_Evolution_Not_Revolution.md --- ch05/00_Evolution_Not_Revolution.md | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/ch05/00_Evolution_Not_Revolution.md b/ch05/00_Evolution_Not_Revolution.md index b2a58702..b4c5ebd6 100644 --- a/ch05/00_Evolution_Not_Revolution.md +++ b/ch05/00_Evolution_Not_Revolution.md @@ -1,18 +1,32 @@ +《《《 [返回首页](../README.md)
+《《《 [上一节](../ch04/05_Conclusions.md) + ## 进化,而不是革命 -支持 `Java` 泛型设计的一个格言是进化,而不是革命。必须有可能迁移大量现有的代码,逐步使用泛型(演化),而不需要进行一次性的全面变革(革命)。泛型设计可确保旧代码针对新的 `Java` 库进行编译,避免了一半代码需要旧库和一半代码需要新库的不幸情况。 +支持 `Java` 泛型设计的一个格言是进化,而不是革命。必须有可能迁移大量现有的代码,逐步使用泛型(演化),而不需要进行一次性的全面变革(革命)。泛型设计可确 +保旧代码针对新的 `Java` 库进行编译,避免了一半代码需要旧库和一半代码需要新库的不幸情况。 -进化要求比通常的向后兼容要强得多。通过简单的向后兼容性,可以为每个应用程序提供传统版本和通用版本;例如,这正是 `C#` 中发生的情况。如果您正在构建由多个供应商提供的代码之上,其中一些人使用旧版集合,其中一些人使用通用集合,这可能会迅速导致版本化的噩梦。 +进化要求比通常的向后兼容要强得多。通过简单的向后兼容性,可以为每个应用程序提供传统版本和通用版本;例如,这正是 `C#` 中发生的情况。如果您正在构建由多个供 +应商提供的代码之上,其中一些人使用旧版集合,其中一些人使用通用集合,这可能会迅速导致版本化的噩梦。 -我们要求的是相同的客户端代码可以同时适用于库的传统版本和通用版本。这意味着图书馆的供应商和客户可以完全独立地选择何时从传统代码转换为通用代码。这比后向兼容性要求强得多;它被称为迁移兼容性或平台兼容性。 +我们要求的是相同的客户端代码可以同时适用于库的传统版本和通用版本。这意味着图书馆的供应商和客户可以完全独立地选择何时从传统代码转换为通用代码。这比后向兼 +容性要求强得多;它被称为迁移兼容性或平台兼容性。 -`Java` 通过擦除来实现泛型,这可以确保传统版本和通用版本通常会生成相同的类文件,并保存一些有关类型的辅助信息。可以用通用类文件替换旧类文件而不更改甚至重新编译任何客户端码;这被称为二进制兼容性。 +`Java` 通过擦除来实现泛型,这可以确保传统版本和通用版本通常会生成相同的类文件,并保存一些有关类型的辅助信息。可以用通用类文件替换旧类文件而不更改甚至重 +新编译任何客户端码;这被称为二进制兼容性。 我们总结这与座右铭二进制兼容性确保迁移兼容性- 或者更简洁一点,擦除可以简化演变过程。 -本节介绍如何将泛型添加到现有代码;它考虑了一个小例子,一个用于扩展集合框架的堆栈库以及一个关联的客户端。我们从传统堆栈库和客户端(在泛型之前为Java编写)开始,然后展示相应的通用库和客户端(为泛型编写的 `Java`)。我们的示例代码很小,因此很容易一次性更新为泛型,但实际上库和客户端会更大,我们可能需要分别进行演变。这是原始类型的帮助,它们是参数化类型的传统对应物。 +本节介绍如何将泛型添加到现有代码;它考虑了一个小例子,一个用于扩展集合框架的堆栈库以及一个关联的客户端。我们从传统堆栈库和客户端(在泛型之前为 `Java` 编 +写)开始,然后展示相应的通用库和客户端(为泛型编写的 `Java`)。我们的示例代码很小,因此很容易一次性更新为泛型,但实际上库和客户端会更大,我们可能需要分 +别进行演变。这是原始类型的帮助,它们是参数化类型的传统对应物。 -程序的各个部分可以按照任意顺序进行演变。你可能有一个遗留客户端的通用库;对于那些使用 `Java 5`中的集合框架和遗留代码的人来说,这是常见的情况。或者你可能有一个通用客户端的遗留库;这种情况下,您希望为库提供通用签名而不需要重写整个库。我们考虑三种方法来做到这一点:对源代码,存根文件和包装器进行最小限度的更改。当你有权访问源代码时,第一个是有用的,第二个是不用的;我们建议不要第三个。 +程序的各个部分可以按照任意顺序进行演变。你可能有一个遗留客户端的通用库;对于那些使用 `Java 5` 中的集合框架和遗留代码的人来说,这是常见的情况。或者你可能 +有一个通用客户端的遗留库;这种情况下,您希望为库提供通用签名而不需要重写整个库。我们考虑三种方法来做到这一点:对源代码,存根文件和包装器进行最小限度的更 +改。当你有权访问源代码时,第一个是有用的,第二个是不用的;我们建议不要第三个。 -在实践中,类库和客户端可能涉及很多接口和类,甚至可能在图书馆和客户端之间没有明确的区别。但是这里讨论的相同原则仍然适用,并且可以用来独立于任何其他部分发展程序的任何部分。 +在实践中,类库和客户端可能涉及很多接口和类,甚至可能在图书馆和客户端之间没有明确的区别。但是这里讨论的相同原则仍然适用,并且可以用来独立于任何其他部分发 +展程序的任何部分。 +《《《 [下一节](01_Reifiable_Types.md)
+《《《 [返回首页](../README.md) From e8acdc91f165f6c3d09795ebf70f793fdf194388 Mon Sep 17 00:00:00 2001 From: maskleo Date: Sun, 15 Apr 2018 18:03:26 +0800 Subject: [PATCH 05/21] Update 00_Evolution_Not_Revolution.md --- ch05/00_Evolution_Not_Revolution.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ch05/00_Evolution_Not_Revolution.md b/ch05/00_Evolution_Not_Revolution.md index b4c5ebd6..6a2db917 100644 --- a/ch05/00_Evolution_Not_Revolution.md +++ b/ch05/00_Evolution_Not_Revolution.md @@ -1,5 +1,5 @@ 《《《 [返回首页](../README.md)
-《《《 [上一节](../ch04/05_Conclusions.md) +《《《 [上一节](../ch04/04_How_Erasure_Works.md) ## 进化,而不是革命 @@ -28,5 +28,5 @@ 在实践中,类库和客户端可能涉及很多接口和类,甚至可能在图书馆和客户端之间没有明确的区别。但是这里讨论的相同原则仍然适用,并且可以用来独立于任何其他部分发 展程序的任何部分。 -《《《 [下一节](01_Reifiable_Types.md)
+《《《 [下一节](01_Legacy_Library_with_Legacy_Client.md)
《《《 [返回首页](../README.md) From 4ce5a2ed9f2115607a11cbf408ab47f01de417df Mon Sep 17 00:00:00 2001 From: maskleo Date: Sun, 15 Apr 2018 18:05:35 +0800 Subject: [PATCH 06/21] Update 01_Legacy_Library_with_Legacy_Client.md --- ch05/01_Legacy_Library_with_Legacy_Client.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/ch05/01_Legacy_Library_with_Legacy_Client.md b/ch05/01_Legacy_Library_with_Legacy_Client.md index edeade84..b9ddb955 100644 --- a/ch05/01_Legacy_Library_with_Legacy_Client.md +++ b/ch05/01_Legacy_Library_with_Legacy_Client.md @@ -1,6 +1,16 @@ +《《《 [返回首页](../README.md)
+《《《 [上一节](00_Evolution_Not_Revolution.md) + ## 旧版客户端的旧版库 +我们从一个简单的栈库和一个关联的客户端开始,如例 `5-1` 所示。这是为 `Java 1.4` 及其版本的集合框架编写的遗留代码。像集合框架一样,我们将库构造为接口 +`Stack`(类似于 `List`),实现类 `ArrayStack`(类似于 `ArrayList`)和实用类 `Stacks`(类似于 `Collections`)。接口堆栈提供了三种方法:`empty`, +`push` 和 `pop`。实现类 `ArrayStack` 提供了一个没有参数的构造函数,并使用方法 `size`,`add` 和 `remove` 在列表上实现了方法 `empty`,`push`和 +`pop`。流行的主体可以更短 - 而不是将值分配给变量,它可以直接返回 - 但是随着代码的发展,变量的类型如何变化会很有趣。实用程序类只提供一种方法,反向,它从 +一个堆栈重复弹出并推向另一个堆栈。 -我们从一个简单的栈库和一个关联的客户端开始,如例 `5-1`所示。这是为 `Java 1.4`及其版本的集合框架编写的遗留代码。像集合框架一样,我们将库构造为接口 `Stack`(类似于 `List`),实现类 `ArrayStack`(类似于 `ArrayList`)和实用类 `Stacks`(类似于 `Collections`)。接口堆栈提供了三种方法:空,推送和弹出。实现类 `ArrayStack` 提供了一个没有参数的构造函数,并使用方法 `size`,`add` 和 `remove` 在列表上实现了方法 `empty`,`push`和 `pop`。流行的主体可以更短 - 而不是将值分配给变量,它可以直接返回 - 但是随着代码的发展,变量的类型如何变化会很有趣。实用程序类只提供一种方法,反向,它从一个堆栈重复弹出并推向另一个堆栈。 +客户端分配一个堆栈,向其中推入一些整数,然后弹出一个整数,然后将其余的余数转换为新的堆栈。由于这是 `Java 1.4`,所以整数必须在传递到 `push` 时显式装 +箱,并且在由 `pop` 返回时显式解除装箱。 -客户端分配一个堆栈,向其中推入一些整数,然后弹出一个整数,然后将其余的余数转换为新的堆栈。由于这是 `Java 1.4`,所以整数必须在传递到 `push` 时显式装箱,并且在由 `pop` 返回时显式解除装箱。 \ No newline at end of file +《《《 [下一节](02_Generic_Library_with_Generic_Client.md)
+《《《 [返回首页](../README.md) From b475901a2f7bb3aabd522af95cb1374bc9b3c970 Mon Sep 17 00:00:00 2001 From: maskleo Date: Sun, 15 Apr 2018 18:08:02 +0800 Subject: [PATCH 07/21] Update 02_Generic_Library_with_Generic_Client.md --- .../02_Generic_Library_with_Generic_Client.md | 70 +++++++++++-------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/ch05/02_Generic_Library_with_Generic_Client.md b/ch05/02_Generic_Library_with_Generic_Client.md index 96edb7e8..236fc201 100644 --- a/ch05/02_Generic_Library_with_Generic_Client.md +++ b/ch05/02_Generic_Library_with_Generic_Client.md @@ -1,43 +1,51 @@ +《《《 [返回首页](../README.md)
+《《《 [上一节](01_Legacy_Library_with_Legacy_Client.md) + ## 具有通用客户端的通用库 -接下来,我们更新库和客户端以使用泛型,如例 `5-2` 中所示。 这是用于 `Java 5` 及其集合版框架的通用代码。 接口现在接受一个类型参数,变成 `Stack`(类似于 `List`),实现类也变为 `ArrayStack`(类似于 `ArrayList`),但没有添加类型参数 实用工具类 `Stacks`(类似于 `Collections`)。 `push` 和 `pop` 的签名和主体中的 `Object` 类型由类型参数 `E` 替换。 请注意,`ArrayStack` 中的构造函数不需要类型参数。 在实用程序类中,反向方法变为带有参数和 `Stack` 类型结果的泛型方法。 适当的类型参数被添加到客户端,现在隐式装箱和取消装箱。 +接下来,我们更新库和客户端以使用泛型,如例 `5-2` 中所示。 这是用于 `Java 5` 及其集合版框架的通用代码。 接口现在接受一个类型参数,变成 +`Stack`(类似于 `List`),实现类也变为 `ArrayStack`(类似于 `ArrayList`),但没有添加类型参数 实用工具类 `Stacks`(类似于 +`Collections`)。 `push` 和 `pop` 的签名和主体中的 `Object` 类型由类型参数 `E` 替换。 请注意,`ArrayStack` 中的构造函数不需要类型参数。 在实用程 +序类中,反向方法变为带有参数和 `Stack` 类型结果的泛型方法。 适当的类型参数被添加到客户端,现在隐式装箱和取消装箱。 -简而言之,转换过程非常简单:只需添加一些类型参数,并用适当的类型变量替换 `Object` 的出现即可。 通过比较两个示例中突出显示的部分,可以发现传统版本和通用版本之间的所有差异。 泛型的实现设计为使两个版本生成基本上相同的类文件。 有些类型的辅助信息可能会有所不同,但要执行的实际字节码将是相同的。 因此,执行遗留版本和通用版本会产生相同的结果。 正如我们接下来讨论的那样,传统和普通资源产生相同类文件的事实可以简化进化过程。 +简而言之,转换过程非常简单:只需添加一些类型参数,并用适当的类型变量替换 `Object` 的出现即可。 通过比较两个示例中突出显示的部分,可以发现传统版本和 +通用版本之间的所有差异。 泛型的实现设计为使两个版本生成基本上相同的类文件。 有些类型的辅助信息可能会有所不同,但要执行的实际字节码将是相同的。 因此, +执行遗留版本和通用版本会产生相同的结果。 正如我们接下来讨论的那样,传统和普通资源产生相同类文件的事实可以简化进化过程。 例 `5-1`。 传统客户端的旧版库 ```java l/Stack.java: - interface Stack { - public boolean empty(); - public void push(Object elt); - public Object pop(); - } + interface Stack { + public boolean empty(); + public void push(Object elt); + public Object pop(); + } l/ArrayStack.java: - import java.util.*; - class ArrayStack implements Stack { - private List list; - public ArrayStack() { list = new ArrayList(); } - public boolean empty() { return list.size() == 0; } - public void push(Object elt) { list.add(elt); } - public Object pop() { - Object elt = list.remove(list.size()-1); - return elt; - } - public String toString() { return "stack"+list.toString(); } - } + import java.util.*; + class ArrayStack implements Stack { + private List list; + public ArrayStack() { list = new ArrayList(); } + public boolean empty() { return list.size() == 0; } + public void push(Object elt) { list.add(elt); } + public Object pop() { + Object elt = list.remove(list.size()-1); + return elt; + } + public String toString() { return "stack"+list.toString(); } + } l/Stacks.java: - class Stacks { - public static Stack reverse(Stack in) { - Stack out = new ArrayStack(); - while (!in.empty()) { - Object elt = in.pop(); - out.push(elt); - } - return out; - } - } - l/Client.java: + class Stacks { + public static Stack reverse(Stack in) { + Stack out = new ArrayStack(); + while (!in.empty()) { + Object elt = in.pop(); + out.push(elt); + } + return out; + } + } + l/Client.java: class Client { public static void main(String[] args) { Stack stack = new ArrayStack(); @@ -52,3 +60,5 @@ } ``` +《《《 [下一节](03_Generic_Library_with_Legacy_Client.md)
+《《《 [返回首页](../README.md) From 633c2d15e2fe654a9d96b6d11d12e82934052af0 Mon Sep 17 00:00:00 2001 From: maskleo Date: Sun, 15 Apr 2018 18:12:42 +0800 Subject: [PATCH 08/21] Update 03_Generic_Library_with_Legacy_Client.md --- ch05/03_Generic_Library_with_Legacy_Client.md | 47 ++++++++++--------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/ch05/03_Generic_Library_with_Legacy_Client.md b/ch05/03_Generic_Library_with_Legacy_Client.md index e4b0e4fc..280cf6c3 100644 --- a/ch05/03_Generic_Library_with_Legacy_Client.md +++ b/ch05/03_Generic_Library_with_Legacy_Client.md @@ -1,3 +1,6 @@ +《《《 [返回首页](../README.md)
+《《《 [上一节](02_Generic_Library_with_Generic_Client.md) + ## 具有传统客户端的通用库 现在让我们考虑一下这种情况,即在客户端保留其旧版本时,库更新为泛型。这可能是因为没有足够的时间一次转换所有内容,或者因为类库和客户由不同的组织控制。 @@ -28,15 +31,15 @@ ```java g/Stack.java: - interface Stack { - public boolean empty(); - public void push(E elt); - public E pop(); - } + interface Stack { + public boolean empty(); + public void push(E elt); + public E pop(); + } g/ArrayStack.java: - import java.util.*; - class ArrayStack implements Stack { + import java.util.*; + class ArrayStack implements Stack { private List list; public ArrayStack() { list = new ArrayList(); } public boolean empty() { return list.size() == 0; } @@ -48,7 +51,7 @@ public String toString() { return "stack"+list.toString(); } } - g/Stacks.java: + g/Stacks.java: class Stacks { public static Stack reverse(Stack in) { Stack out = new ArrayStack(); @@ -60,19 +63,19 @@ } } - g/Client.java: - class Client { - public static void main(String[] args) { - Stack stack = new ArrayStack(); - for (int i = 0; i<4; i++) stack.push(i); - assert stack.toString().equals("stack[0, 1, 2, 3]"); - int top = stack.pop(); - assert top == 3 && stack.toString().equals("stack[0, 1, 2]"); - Stack reverse = Stacks.reverse(stack); - assert stack.empty(); - assert reverse.toString().equals("stack[2, 1, 0]"); - } - } + g/Client.java: + class Client { + public static void main(String[] args) { + Stack stack = new ArrayStack(); + for (int i = 0; i<4; i++) stack.push(i); + assert stack.toString().equals("stack[0, 1, 2, 3]"); + int top = stack.pop(); + assert top == 3 && stack.toString().equals("stack[0, 1, 2]"); + Stack reverse = Stacks.reverse(stack); + assert stack.empty(); + assert reverse.toString().equals("stack[2, 1, 0]"); + } + } ``` 如果我们遵循上面的建议,并在启用适当的开关的情况下重新运行编译器,我们会得到更多的细节: @@ -130,3 +133,5 @@ 这编译了遗留代码并且没有发出警告或错误。 这种关闭警告的方法只适用于真正的遗留代码,没有 `Java 5` 中引入的通用功能或其他功能。 也可以使用注释关闭未经 检查的警告,如下一节所述,即使使用 `Java 5` 中引入的功能,也可以使用这些警告。 +《《《 [下一节](04_Legacy_Library_with_Generic_Client.md)
+《《《 [返回首页](../README.md) From 30c538f6a623b28e6d39d8d499bf1e6ea8db003a Mon Sep 17 00:00:00 2001 From: maskleo Date: Sun, 15 Apr 2018 18:14:05 +0800 Subject: [PATCH 09/21] Update 04_Legacy_Library_with_Generic_Client.md --- ch05/04_Legacy_Library_with_Generic_Client.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ch05/04_Legacy_Library_with_Generic_Client.md b/ch05/04_Legacy_Library_with_Generic_Client.md index 2b093978..d224cdbb 100644 --- a/ch05/04_Legacy_Library_with_Generic_Client.md +++ b/ch05/04_Legacy_Library_with_Generic_Client.md @@ -1,3 +1,6 @@ +《《《 [返回首页](../README.md)
+《《《 [上一节](03_Generic_Library_with_Legacy_Client.md) + ## 具有通用客户端的旧版库 在客户端之前更新库通常是有意义的,但可能会出现您希望以其他方式进行更新的情况。 例如,您可能负责维护客户而不是类库; 或者类库可能很大,因此您可能需要逐 @@ -236,3 +239,5 @@ 通过确保遗留对象和泛型对象相同,`Java` 泛型的设计避免了包装器的所有这些问题。 `C#` 泛型的设计有很大的不同:遗留类和泛型类是完全不同的,任何结合遗留 集合和泛型集合的尝试都会碰到这里讨论的包装器的困难。 +《《《 [下一节](05_Conclusions.md)
+《《《 [返回首页](../README.md) From 089fd0bb87d7431f5098aba2f2abbe023bac3f3d Mon Sep 17 00:00:00 2001 From: maskleo Date: Sun, 15 Apr 2018 18:14:57 +0800 Subject: [PATCH 10/21] Update 05_Conclusions.md --- ch05/05_Conclusions.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ch05/05_Conclusions.md b/ch05/05_Conclusions.md index 777bbbe5..2adf4893 100644 --- a/ch05/05_Conclusions.md +++ b/ch05/05_Conclusions.md @@ -1,3 +1,6 @@ +《《《 [返回首页](../README.md)
+《《《 [上一节](04_Legacy_Library_with_Generic_Client.md) + ## 结论 回顾一下,我们已经看到了库和客户端的通用版本和旧版本。这些生成等效的类文件,这大大简化了进化。您可以使用具有旧版客户端的通用库,或者使用通用客户端的旧版 @@ -10,3 +13,6 @@ 中,泛型类型和数组在运行时都包含有关参数和元素类型的信息。每种方法都有优点和缺点。在下一章中,我们将讨论由于 `Java` 没有提供有关类型参数的信息而导致的转 换和数组问题,并且这些问题在C#中不会出现。另一方面,`C#` 中的进化要困难得多。遗留和泛型集合类是完全不同的,任何将遗留集合和泛型集合结合起来的尝试都会遇 到前面讨论过的包装器的困难。相反,正如我们所看到的,`Java` 中的演变很简单。 + +《《《 [下一节](../ch06/00_Reification.md)
+《《《 [返回首页](../README.md) From 220463a5c51bf3b92f5edb54651994d672429130 Mon Sep 17 00:00:00 2001 From: maskleo Date: Sun, 15 Apr 2018 18:21:55 +0800 Subject: [PATCH 11/21] Update 00_Reification.md --- ch06/00_Reification.md | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/ch06/00_Reification.md b/ch06/00_Reification.md index e8614a54..44fc1537 100644 --- a/ch06/00_Reification.md +++ b/ch06/00_Reification.md @@ -1,8 +1,20 @@ +《《《 [返回首页](../README.md)
+《《《 [上一节](../ch05/05_Conclusions.md) -“牛津英语词典”将 `reify` 定义如下:“将精神转化为事物;物化“。具有相同含义的一个平庸词是 `thingify` 。在计算中,物化已经意味着一种类型的显式表示 - 即运行时类型信息。在 `Java` 中,数组提供有关其组件类型的信息,而泛型不提供有关其类型参数的信息。 +## 具体化 -在某种意义上,上一章是关于不改变参数类型的优点。传统代码不会区分 `List` 和 `List` 和 `List>`,因此不能对参数类型进行重新定义,这对于缓解进化和促进遗留代码和新代码之间的兼容性至关重要。 +“牛津英语词典”将 `reify` 定义如下:“将精神转化为事物;物化“。具有相同含义的一个平庸词是 `thingify` 。在计算中,具体化已经意味着一种类型的显式表示 - 即 +运行时类型信息。在 `Java` 中,数组提供有关其组件类型的信息,而泛型不提供有关其类型参数的信息。 -但现在是支付吹笛者的时候了。物化在 `Java` 的某些方面起着至关重要的作用,并且缺乏有利于进化的物化也必然导致一些粗糙的边缘。本章警告您有限制并介绍一些变通方法。本章几乎完全处理了你可能不希望你不需要知道的事情 - 事实上,如果你从不使用泛型类型进行强制转换,实例测试,异常或数组,那么你不太可能需要这里介绍的材料。 +在某种意义上,上一章是关于不改变参数类型的优点。传统代码不会区分 `List` 和 `List` 和 `List>`,因此不能对参数类型进行 +重新定义,这对于缓解进化和促进遗留代码和新代码之间的兼容性至关重要。 -我们从一个精确的定义开始,说明一个类型在 `Java` 中的可定义性。然后我们考虑与实例相关的角落案例,包括实例测试和强制转换,异常和数组。数组和泛型之间的合适性是语言中最糟糕的粗糙角落,我们用广告真理原理和不雅曝光原则来说明如何避免最糟糕的陷阱。 \ No newline at end of file +但现在是支付吹笛者的时候了。物化在 `Java` 的某些方面起着至关重要的作用,并且缺乏有利于进化的物化也必然导致一些粗糙的边缘。本章警告您有限制并介绍一些变通 +方法。本章几乎完全处理了你可能不希望你不需要知道的事情 - 事实上,如果你从不使用泛型类型进行强制转换,实例测试,异常或数组,那么你不太可能需要这里介绍的 +材料。 + +我们从一个精确的定义开始,说明一个类型在 `Java` 中的可定义性。然后我们考虑与实例相关的角落案例,包括实例测试和强制转换,异常和数组。数组和泛型之间的合适 +性是语言中最糟糕的粗糙角落,我们用广告真理原理和不雅曝光原则来说明如何避免最糟糕的陷阱。 + +《《《 [下一节](01_Reifiable_Types.md)
+《《《 [返回首页](../README.md) From 00f828f7521f9fbb8bb0c2e303a13c7b4f70ba29 Mon Sep 17 00:00:00 2001 From: maskleo Date: Sun, 15 Apr 2018 18:23:00 +0800 Subject: [PATCH 12/21] Update 01_Reifiable_Types.md --- ch06/01_Reifiable_Types.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/ch06/01_Reifiable_Types.md b/ch06/01_Reifiable_Types.md index d7b7b146..d528d930 100644 --- a/ch06/01_Reifiable_Types.md +++ b/ch06/01_Reifiable_Types.md @@ -1,6 +1,12 @@ +《《《 [返回首页](../README.md)
+《《《 [上一节](00_Reification.md) + ## 可定义类型 -在 `Java` 中,数组的类型是用其组件类型来表示的,而参数化类型的类型是在没有其类型参数的情况下被指定的。 例如,一个数组的数组将带有指定类型 `Number []`,而一个数字列表将带有指定类型 `ArrayList`,而不是 `ArrayList`; 原始类型,而不是参数化类型被通用化。 当然,列表中的每个元素都会附加一个指定类型 - 比如整数或双精度 - 但这与指定参数类型并不相同。 如果列表中的每个元素都是整数,我们将无法判断是否有 `ArrayList`,`ArrayList` 或 `ArrayList`; 如果列表为空,我们将无法确定它是什么样的空列表。 +在 `Java` 中,数组的类型是用其组件类型来表示的,而参数化类型的类型是在没有其类型参数的情况下被指定的。 例如,一个数组的数组将带有指定类型 +`Number []`,而一个数字列表将带有指定类型 `ArrayList`,而不是 `ArrayList`; 原始类型,而不是参数化类型被通用化。 当然,列表中的每个元素都会附 +加一个指定类型 - 比如整数或双精度 - 但这与指定参数类型并不相同。 如果列表中的每个元素都是整数,我们将无法判断是否有 `ArrayList`, +`ArrayList` 或 `ArrayList`; 如果列表为空,我们将无法确定它是什么样的空列表。 在 `Java` 中,如果类型在运行时完全表示,即擦除不会删除任何有用的信息,那么我们说类型是可重用的。 准确地说,如果是以下类型之一,则类型是可验证的: @@ -13,4 +19,7 @@ - 带有实际参数的参数化类型(例如 `List`,`ArrayList` 或 `Map`) - 带有边界的参数化类型(例如 `List` 或 `Comparable `) -所以类型 `List` 不可赋值,尽管它与 `List` 等效。 以这种方式定义可定义类型使得它们易于在语法上识别。 \ No newline at end of file +所以类型 `List` 不可赋值,尽管它与 `List` 等效。 以这种方式定义可定义类型使得它们易于在语法上识别。 + +《《《 [下一节](02_Instance_Tests_and_Casts.md)
+《《《 [返回首页](../README.md) From afe58f444562d85a521669c03e29a36f3b8e9d41 Mon Sep 17 00:00:00 2001 From: maskleo Date: Sun, 15 Apr 2018 18:25:54 +0800 Subject: [PATCH 13/21] Update 02_Instance_Tests_and_Casts.md --- ch06/02_Instance_Tests_and_Casts.md | 177 +++++++--------------------- 1 file changed, 41 insertions(+), 136 deletions(-) diff --git a/ch06/02_Instance_Tests_and_Casts.md b/ch06/02_Instance_Tests_and_Casts.md index 5d74e661..8aed3dcc 100644 --- a/ch06/02_Instance_Tests_and_Casts.md +++ b/ch06/02_Instance_Tests_and_Casts.md @@ -1,3 +1,6 @@ +《《《 [返回首页](../README.md)
+《《《 [上一节](01_Reifiable_Types.md) + ## 实例测试和示例 实例测试和示例取决于运行时检查的类型,因此取决于具体化。 因此,针对不可修饰类型的实例测试会报告错误,并且对不可修饰类型进行强制转换通常会发出警告。 @@ -18,7 +21,8 @@ } ``` -`equals` 方法接受 `Object` 类型的参数,检查对象是否为 `Integer` 类的实例,如果是,则将其转换为 `Integer` 并比较两个整数的值。 此代码可用,因为 `Integer` 是可重用类型:运行时可以使用检查对象是否为 `Integer` 实例所需的所有信息。 +`equals` 方法接受 `Object` 类型的参数,检查对象是否为 `Integer` 类的实例,如果是,则将其转换为 `Integer` 并比较两个整数的值。 此代码可用,因为 +`Integer` 是可重用类型:运行时可以使用检查对象是否为 `Integer` 实例所需的所有信息。 现在考虑一下如何在列表上定义相等性,就像 `java.util` 中的 `AbstractList` 类一样。 定义这个的自然但不正确的方式如下: @@ -42,9 +46,12 @@ } ``` -同样,`equals` 方法接受 `Object` 类型的参数,检查对象是否为 `List` 类型的实例,如果是,则将其转换为 `List` 并比较两个列表中的相应元素。此代码不起作用,因为 `List` 不是可重用类型:检查对象是否为 `List`的实例所需的某些信息在运行时不可用。您可以测试一个对象是否实现接口 `List`,但不能测试其类型参数是否为 `E`。事实上,有关E的信息缺少双倍的数据,因为它不适用于接收方或 `equals` 方法的参数。 +同样,`equals` 方法接受 `Object` 类型的参数,检查对象是否为 `List` 类型的实例,如果是,则将其转换为 `List` 并比较两个列表中的相应元素。此代 +码不起作用,因为 `List` 不是可重用类型:检查对象是否为 `List`的实例所需的某些信息在运行时不可用。您可以测试一个对象是否实现接口 `List`,但不 +能测试其类型参数是否为 `E`。事实上,有关 `E` 的信息缺少双倍的数据,因为它不适用于接收方或 `equals` 方法的参数。 -(即使这个代码有效,还有一个问题,列表上的相等约定没有提及类型,如果 `List` 包含相同的值,那么它们可能与 `List` 相等。例如,`[1,2,3]` 应该与自身相等,无论它是否被视为整数列表或对象列表。) +(即使这个代码有效,还有一个问题,列表上的相等约定没有提及类型,如果 `List` 包含相同的值,那么它们可能与 `List` 相等。例如, +`[1,2,3]` 应该与自身相等,无论它是否被视为整数列表或对象列表。) 编译上面的代码报告了两个问题,一个是实例测试的错误,另一个是未经检查的演员警告: @@ -62,7 +69,8 @@ 1 warning ``` -实例检查报告错误,因为没有可能的方法来测试给定对象是否属于类型 `List`。 演员报告未经检查的警告; 它将执行转换,但它不能检查列表元素实际上是否为 `E` 类型。 +实例检查报告错误,因为没有可能的方法来测试给定对象是否属于类型 `List`。 演员报告未经检查的警告; 它将执行转换,但它不能检查列表元素实际上是否为 +`E` 类型。 为了解决这个问题,我们用可调整类型 `List` 替换了不可保留类型 `List`。 这是一个更正的定义(再次,从实际来源稍微简化): @@ -87,11 +95,15 @@ } ``` -除了更改实例测试和强制类型外,第二个迭代器的类型也从 `Iterator` 更改为 `Iterator`,第二个元素的类型从 `E` 更改为 `Object`。 代码类型检查,因为即使第二个迭代器的元素类型是未知的,它也保证它必须是 `Object` 的一个子类型,并且对 `equals` 的嵌套调用只需要它的第二个参数是一个对象。 +除了更改实例测试和强制类型外,第二个迭代器的类型也从 `Iterator` 更改为 `Iterator`,第二个元素的类型从 `E` 更改为 `Object`。 代码类型检查, +因为即使第二个迭代器的元素类型是未知的,它也保证它必须是 `Object` 的一个子类型,并且对 `equals` 的嵌套调用只需要它的第二个参数是一个对象。 (这段代码正确地满足列表上的等式约定,现在如果一个 `List` 包含相同的值,那么它们将等于一个 `List`。) -替代修复是可能的。 而不是通配符类型 `List` 和 `Iterator`,您可以使用原始类型 `List` 和 `Iterator`,这些类型也是可验证的。 我们建议使用无界通配符类型而不是原始类型,因为它们提供了更强的静态类型保证; 如果您使用原始类型,那么在使用无界通配符时只会将标记为警告的许多错误标记为警告。 此外,您可以将第一个迭代器的声明更改为 `Iterator`,将第一个元素的声明更改为 `Object`,以使它们与第二个迭代器匹配,并且代码仍然会进行类型检查。 我们建议始终使用尽可能具体的类型声明; 这有助于编译器捕获更多错误并编译更高效的代码。 +替代修复是可能的。 而不是通配符类型 `List` 和 `Iterator`,您可以使用原始类型 `List` 和 `Iterator`,这些类型也是可验证的。 我们建议使用无界 +通配符类型而不是原始类型,因为它们提供了更强的静态类型保证; 如果您使用原始类型,那么在使用无界通配符时只会将标记为警告的许多错误标记为警告。 此外,您 +可以将第一个迭代器的声明更改为 `Iterator`,将第一个元素的声明更改为 `Object`,以使它们与第二个迭代器匹配,并且代码仍然会进行类型检查。 我们建议 +始终使用尽可能具体的类型声明; 这有助于编译器捕获更多错误并编译更高效的代码。 **不可接受的转换**对不可转换类型的实例测试通常是错误的。 但是,在某些情况下,转换为不可赋予的类型是允许的。 @@ -106,11 +118,14 @@ } ``` -编译此代码将成功,不会出现错误或警告。 实例测试没有错误,因为 `List` 是可重用的类型。 由于转换源的类型为 `Collection`,转换不会报告警告,并且任何实现了接口 `List` 的类型的对象实际上都必须具有 `List` 类型。 +编译此代码将成功,不会出现错误或警告。 实例测试没有错误,因为 `List` 是可重用的类型。 由于转换源的类型为 `Collection`,转换不会报告警告,并且 +任何实现了接口 `List` 的类型的对象实际上都必须具有 `List` 类型。 -**未经检查的强制转换**编译器很少能够确定如果强制转换为不可继承类型,则必须产生该类型的值。在其余情况下,对不可修饰的类型进行强制转换时将标记未经检查的警告,而针对不可修饰类型的实例测试始终会被捕获为错误。这是因为对于无法执行的实例测试而言,从来没有任何意义,但是可能存在无法检查的强制转换。 +**未经检查的强制转换**编译器很少能够确定如果强制转换为不可继承类型,则必须产生该类型的值。在其余情况下,对不可修饰的类型进行强制转换时将标记未经检查 +的警告,而针对不可修饰类型的实例测试始终会被捕获为错误。这是因为对于无法执行的实例测试而言,从来没有任何意义,但是可能存在无法检查的强制转换。 -类型系统推断出程序的事实 - 例如,某个变量总是包含一个字符串列表。但是没有哪种类型的系统是完美的总会有一些程序员可以推论出来的事实,但是类型系统却没有。为了允许程序员在这种情况下找到解决方法,编译器在执行某些强制转换时发出警告而不是错误。 +类型系统推断出程序的事实 - 例如,某个变量总是包含一个字符串列表。但是没有哪种类型的系统是完美的总会有一些程序员可以推论出来的事实,但是类型系统却没 +有。为了允许程序员在这种情况下找到解决方法,编译器在执行某些强制转换时发出警告而不是错误。 例如,下面是将对象列表提升为字符串列表的代码,如果对象列表仅包含字符串,则会抛出类转换异常: @@ -138,7 +153,8 @@ 如果任何对象不是字符串,该方法会在对象列表上抛出循环并抛出类抛出异常。 因此,当方法的最后一行到达时,将对象列表转换为字符串列表是安全的。 -但编译器无法推断出这一点,因此程序员必须使用未经检查的 `cast`。将对象列表转换为字符串列表是非法的,因此必须分两步进行。 首先,将对象列表转换为通配符类型列表; 这个演员是安全的。 其次,将通配符类型列表转换为字符串列表; 此演员阵容是允许的,但会产生未经检查的警告: +但编译器无法推断出这一点,因此程序员必须使用未经检查的 `cast`。将对象列表转换为字符串列表是非法的,因此必须分两步进行。 首先,将对象列表转换为通配符 +类型列表; 这个演员是安全的。 其次,将通配符类型列表转换为字符串列表; 此演员阵容是允许的,但会产生未经检查的警告: ```java % javac -Xlint:unchecked Promote.java @@ -150,137 +166,26 @@ 1 warning ``` -测试代码将该方法应用于两个列表,一个仅包含字符串(因此成功),另一个包含整数(因此引发异常)。在第一个断言中,为了比较对象列表和字符串列表,我们必须首先将两个类型转换为类型 `List`(这个转换是安全的),因为尝试比较列表中的对象列表和字符串列表会产生一个类型错误。 +测试代码将该方法应用于两个列表,一个仅包含字符串(因此成功),另一个包含整数(因此引发异常)。在第一个断言中,为了比较对象列表和字符串列表,我们必须 +首先将两个类型转换为类型 `List`(这个转换是安全的),因为尝试比较列表中的对象列表和字符串列表会产生一个类型错误。 -如果原始列表仅包含字符串,则可以使用完全相同的技术将原始列表提升为字符串列表。这种技术对于将遗留代码和泛型代码进行拟合很重要,也是使用擦除来实现泛型的主要原因之一。相关技术在第 `8.1` 节中讨论。 +如果原始列表仅包含字符串,则可以使用完全相同的技术将原始列表提升为字符串列表。这种技术对于将遗留代码和泛型代码进行拟合很重要,也是使用擦除来实现泛型 +的主要原因之一。相关技术在第 `8.1` 节中讨论。 在 `5.4.1` 节中,我们需要对元素类型(`E`)进行未经检查的转换,以使传统add方法返回的值的类型与其泛型相匹配签名。 -您应该尽量减少代码中未经检查的强制转换次数,但有时候,如上所述,它们是无法避免的。在本书中,我们遵循这样一个惯例,即我们总是将评论放在包含演员阵容的一行上,以表明这是一种有意的解决方法,而不是无意中的滑动;我们建议你也这样做。将注释放在与转换相同的行上非常重要,以便在扫描编译器发出的警告时很容易确认每行都包含注释。如果没有,那么你应该把警告等同于一个错误! - -如果某个方法故意包含未经检查的强制转换,则可能希望在注释之前添加注释 `@SuppressWarnings(“unchecked”)` 以避免虚假警告。 我们在 `5.4.1` 节看到了这种技术的应用。 - -作为使用未经检查的强制转换的另一个例子,在第 `6.5` 节中,我们将看到使用类型为 `Object[]` 的未经检查强制类型转换为 `T []` 的代码。 由于对象数组的创建方式,事实上,它确保数组始终具有正确的类型。 - -在 `C` 中(和它的后代 `C++`)未经检查的强制转换比在 `Java` 中未经检查的强制转换危险得多。 与 `C` 不同,即使存在未经检查的强制转换,`Java` 运行时也能保证重要的安全属性; 例如,绝不允许使用数组边界之外的索引访问数组。 尽管如此,在 `Java` 中未经检查的强制转换是一种解决方法,应谨慎使用。 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +您应该尽量减少代码中未经检查的强制转换次数,但有时候,如上所述,它们是无法避免的。在本书中,我们遵循这样一个惯例,即我们总是将评论放在包含演员阵容的 +一行上,以表明这是一种有意的解决方法,而不是无意中的滑动;我们建议你也这样做。将注释放在与转换相同的行上非常重要,以便在扫描编译器发出的警告时很容易确 +认每行都包含注释。如果没有,那么你应该把警告等同于一个错误! +如果某个方法故意包含未经检查的强制转换,则可能希望在注释之前添加注释 `@SuppressWarnings(“unchecked”)` 以避免虚假警告。 我们在 `5.4.1` 节看到了这 +种技术的应用。 +作为使用未经检查的强制转换的另一个例子,在第 `6.5` 节中,我们将看到使用类型为 `Object[]` 的未经检查强制类型转换为 `T []` 的代码。 由于对象数组的创 +建方式,事实上,它确保数组始终具有正确的类型。 +在 `C` 中(和它的后代 `C++`)未经检查的强制转换比在 `Java` 中未经检查的强制转换危险得多。 与 `C` 不同,即使存在未经检查的强制转换,`Java` 运行时也 +能保证重要的安全属性; 例如,绝不允许使用数组边界之外的索引访问数组。 尽管如此,在 `Java` 中未经检查的强制转换是一种解决方法,应谨慎使用。 +《《《 [下一节](03_Exception_Handling.md)
+《《《 [返回首页](../README.md) From 243b26cd4a5c6214d846b5c117b9a0ef1c65bc7d Mon Sep 17 00:00:00 2001 From: maskleo Date: Sun, 15 Apr 2018 18:27:07 +0800 Subject: [PATCH 14/21] Update 03_Exception_Handling.md --- ch06/03_Exception_Handling.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ch06/03_Exception_Handling.md b/ch06/03_Exception_Handling.md index 58c8c622..14fe53f1 100644 --- a/ch06/03_Exception_Handling.md +++ b/ch06/03_Exception_Handling.md @@ -1,8 +1,11 @@ +《《《 [返回首页](../README.md)
+《《《 [上一节](02_Instance_Tests_and_Casts.md) + ## 异常处理 -在 `try` 语句中,每个 `catch` 子句检查抛出的异常是否与给定的类型匹配。 这与实例测试执行的检查相同,因此也适用相同的限制:该类型必须是可验证的。 此外, -`catch` 子句中的类型必须是 `Throwable` 的子类。 由于创建不能出现在 `catch` 子句中的 `Throwable` 的子类没什么意义,因此如果您尝试创建 `Throwable` -的参数化子类,`Java` 编译器会发出警告。 +在 `try` 语句中,每个 `catch` 子句检查抛出的异常是否与给定的类型匹配。 这与实例测试执行的检查相同,因此也适用相同的限制:该类型必须是可验证的。 此 +外,`catch` 子句中的类型必须是 `Throwable` 的子类。 由于创建不能出现在 `catch` 子句中的 `Throwable` 的子类没什么意义,因此如果您尝试创建 +`Throwable` 的参数化子类,`Java` 编译器会发出警告。 例如,下面是一个新的异常的允许定义,它包含一个整数值: @@ -85,4 +88,5 @@ 在 `Throwable` 子句中输入变量虽然 `Throwable` 的子类不能是参数化的,但可以在方法声明的 `throws` 子句中使用类型变量。此技术在第 `9.3` 节中说明。 - +《《《 [下一节](04_Array_Creation.md)
+《《《 [返回首页](../README.md) From 55b7b8394cdaccde46a99c67758f26f4f63282d2 Mon Sep 17 00:00:00 2001 From: maskleo Date: Sun, 15 Apr 2018 18:29:25 +0800 Subject: [PATCH 15/21] Update 04_Array_Creation.md --- ch06/04_Array_Creation.md | 75 +++++++++++++-------------------------- 1 file changed, 25 insertions(+), 50 deletions(-) diff --git a/ch06/04_Array_Creation.md b/ch06/04_Array_Creation.md index 2ee5210f..37da4b38 100644 --- a/ch06/04_Array_Creation.md +++ b/ch06/04_Array_Creation.md @@ -1,6 +1,11 @@ +《《《 [返回首页](../README.md)
+《《《 [上一节](03_Exception_Handling.md) + ## 数组创建 -数组通过它们的组件类型来进行通用化,这意味着它们携带有关组件类型的运行时信息。 此实体类型信息用于实例测试和强制转换,还用于检查是否允许分配到数组组件。 +数组通过它们的组件类型来进行通用化,这意味着它们携带有关组件类型的运行时信息。 此实体类型信息用于实例测试和强制转换,还用于检查是否允许分配到数组组 +件。 + 回想一下第 `2.5` 节中的这个例子。 ```java @@ -9,9 +14,11 @@ nums[2] = 3.14; // 数组存储异常 ``` -第一行分配一个新数组,其中指定它是一个整数数组。 第二行将此数组赋给一个包含数字数组的变量; 这是允许的,因为与泛型类型不同,数组是协变的。 第三行的赋值在运行时引发了一个数组存储异常,因为赋值是 `double` 类型的,并且这与附加到数组的指定类型不兼容。 +第一行分配一个新数组,其中指定它是一个整数数组。 第二行将此数组赋给一个包含数字数组的变量; 这是允许的,因为与泛型类型不同,数组是协变的。 第三行的赋 +值在运行时引发了一个数组存储异常,因为赋值是 `double` 类型的,并且这与附加到数组的指定类型不兼容。 -因为数组必须确定其组件类型,所以创建一个新数组是错误的,除非其组件类型是可验证的。 您可能遇到的两个主要问题是数组的类型是一个类型变量,并且数组的类型是参数化类型。 +因为数组必须确定其组件类型,所以创建一个新数组是错误的,除非其组件类型是可验证的。 您可能遇到的两个主要问题是数组的类型是一个类型变量,并且数组的类型 +是参数化类型。 考虑以下(不正确)代码将集合转换为数组: @@ -19,8 +26,8 @@ import java.util.*; class Annoying { public static T[] toArray(Collection c) { - T[] a = new T[c.size()]; // compile-time error - int i=0; for (T x : c) a[i++] = x; +        T[] a = new T[c.size()]; // 编译错误 + int i=0; for (T x : c) a[i++] = x; return a; } } @@ -31,7 +38,7 @@ ```java % javac Annoying.java Annoying.java:4: generic array creation - T[] a = new T[c.size()]; // compile-time error + T[] a = new T[c.size()]; // 编译错误 ^ 1 error ``` @@ -46,7 +53,7 @@ public static List[] twoLists() { List a = Arrays.asList(1,2,3); List b = Arrays.asList(4,5,6); - return new List[] {a, b}; // compile-time error + return new List[] {a, b}; // 编译错误 } } ``` @@ -56,55 +63,23 @@ ```java % javac AlsoAnnoying.java AlsoAnnoying.java:6: generic array creation - return new List[] {a, b}; // compile-time error + return new List[] {a, b}; // 编译错误 ^ 1 error ``` 我们也很快讨论这个问题的解决方法。 -无法创建通用数组是 `Java` 中最严重的限制之一。因为它太讨厌了,所以值得重申其发生的原因:通用数组是有问题的,因为泛型是通过擦除来实现的,但是擦除是有益的,因为它简化了进化。 - -最好的解决方法是使用 `ArrayList` 或来自集合框架的其他类优先于数组。我们在 `2.5` 节中讨论了集合类和数组之间的权衡,并且我们注意到在很多情况下集合比数组更好:因为它们在编译时捕获更多的错误,因为它们提供更多的操作,并且因为它们提供了更多的表示灵活性。到目前为止,数组提供的问题的最佳解决方案是“只是说不”:使用集合优先于数组。 - -有时候这不起作用,因为为了兼容性或效率的原因你需要一个数组。这种情况的例子发生在集合框架中:为了兼容性,`toArray` 方法将集合转换为数组;为了提高效率,通过将列表元素存储在一个数组中来实现类数组列表。我们将在下面的章节中详细讨论这两种情况,以及帮助您避免这些情况的相关陷阱和原则:广告真相原则和不雅暴露原则。我们也考虑可变参数和通用数组的创建带来的问题。 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +无法创建通用数组是 `Java` 中最严重的限制之一。因为它太讨厌了,所以值得重申其发生的原因:通用数组是有问题的,因为泛型是通过擦除来实现的,但是擦除是有 +益的,因为它简化了进化。 +最好的解决方法是使用 `ArrayList` 或来自集合框架的其他类优先于数组。我们在 `2.5` 节中讨论了集合类和数组之间的权衡,并且我们注意到在很多情况下集合比数 +组更好:因为它们在编译时捕获更多的错误,因为它们提供更多的操作,并且因为它们提供了更多的表示灵活性。到目前为止,数组提供的问题的最佳解决方案是“只是说 +不”:使用集合优先于数组。 +有时候这不起作用,因为为了兼容性或效率的原因你需要一个数组。这种情况的例子发生在集合框架中:为了兼容性,`toArray` 方法将集合转换为数组;为了提高效 +率,通过将列表元素存储在一个数组中来实现类数组列表。我们将在下面的章节中详细讨论这两种情况,以及帮助您避免这些情况的相关陷阱和原则:广告真相原则和不 +雅暴露原则。我们也考虑可变参数和通用数组的创建带来的问题。 +《《《 [下一节](05_The_Principle_of_Truth_in_Advertising.md)
+《《《 [返回首页](../README.md) From 377da2b7d991215246d41593f29bf6cc1b2b69b1 Mon Sep 17 00:00:00 2001 From: maskleo Date: Sun, 15 Apr 2018 18:32:19 +0800 Subject: [PATCH 16/21] Update 05_The_Principle_of_Truth_in_Advertising.md --- ...5_The_Principle_of_Truth_in_Advertising.md | 193 ++++-------------- 1 file changed, 42 insertions(+), 151 deletions(-) diff --git a/ch06/05_The_Principle_of_Truth_in_Advertising.md b/ch06/05_The_Principle_of_Truth_in_Advertising.md index 546e35b9..c4e227ff 100644 --- a/ch06/05_The_Principle_of_Truth_in_Advertising.md +++ b/ch06/05_The_Principle_of_Truth_in_Advertising.md @@ -1,6 +1,11 @@ +《《《 [返回首页](../README.md)
+《《《 [上一节](04_Array_Creation.md) + ## 广告中的真理原则 -在前面的章节中我们看到,将集合转换为数组的简单方法将不起作用。 我们可能会尝试的第一个解决方案是添加一个未经检查的演员表,但我们很快会看到,这导致更令人困惑的问题。 正确的解决方案需要我们去反思。 由于将任何泛型结构转换为数组时都会出现相同的问题,因此值得了解这些问题及其解决方案。 我们将研究上一节中的静态 `toArray` 方法的变体; 相同的想法适用于集合框架的 `Collection` 接口中的 `toArray` 方法。 +在前面的章节中我们看到,将集合转换为数组的简单方法将不起作用。 我们可能会尝试的第一个解决方案是添加一个未经检查的演员表,但我们很快会看到,这导致更令 +人困惑的问题。 正确的解决方案需要我们去反思。 由于将任何泛型结构转换为数组时都会出现相同的问题,因此值得了解这些问题及其解决方案。 我们将研究上一节中 +的静态 `toArray` 方法的变体; 相同的想法适用于集合框架的 `Collection` 接口中的 `toArray` 方法。 这里是第二次尝试将集合转换为数组,这次使用未经检查的转换,并添加了测试代码: @@ -19,7 +24,8 @@ } ``` -上一节中的代码使用短语 `new T[c.size()]` 创建数组,导致编译器报告通用数组创建错误。 新代码改为分配一个对象数组并将其转换为 `T []` 类型,这会导致编译器发出未经检查的强制转换警告: +上一节中的代码使用短语 `new T[c.size()]` 创建数组,导致编译器报告通用数组创建错误。 新代码改为分配一个对象数组并将其转换为 `T []` 类型,这会导致编 +译器发出未经检查的强制转换警告: ```java % javac -Xlint Wrong.java @@ -39,9 +45,11 @@ at Wrong.main(Wrong.java:11) ``` -难懂的短语 `[Ljava.lang.Object` 是数组的指定类型,其中 `[L` 表示它是引用类型的数组,而 `java.lang.Object` 是数组的组件类型。 类转换错误消息引用包含对 `toArray` 的调用的行。 此错误消息可能会令人困惑,因为该行看起来不包含演员表! +难懂的短语 `[Ljava.lang.Object` 是数组的指定类型,其中 `[L` 表示它是引用类型的数组,而 `java.lang.Object` 是数组的组件类型。 类转换错误消息引用包 +含对 `toArray` 的调用的行。 此错误消息可能会令人困惑,因为该行看起来不包含演员表! -为了看看这个程序出了什么问题,让我们看看程序是如何使用擦除来翻译的。 删除删除集合和列表中的类型参数,重新设置类型变量 `T` 与 `Object` 的出现位置,并在调用 `toArray` 时插入相应的强制转换,从而生成以下等效代码: +为了看看这个程序出了什么问题,让我们看看程序是如何使用擦除来翻译的。 删除删除集合和列表中的类型参数,重新设置类型变量 `T` 与 `Object` 的出现位置,并 +在调用 `toArray` 时插入相应的强制转换,从而生成以下等效代码: ```java import java.util.*; @@ -58,18 +66,23 @@ } ``` -类型擦除将未选中的转换转换为 `T []` 转换为 `Object []` 的转换,并在调用 `toArray` 时将转换插入 `String []`。 运行时,这些转换中的第一个成功。 但即使该数组只包含字符串,但它的通用类型表明它是一个 `Object` 数组,因此第二次投射失败。 +类型擦除将未选中的转换转换为 `T []` 转换为 `Object []` 的转换,并在调用 `toArray` 时将转换插入 `String []`。 运行时,这些转换中的第一个成功。 但 +即使该数组只包含字符串,但它的通用类型表明它是一个 `Object` 数组,因此第二次投射失败。 为了避免这个问题,你必须坚持以下原则:   > 广告中的真理原则:数组的指定类型必须是其静态类型的擦除子类型。 在 `toArray` 本身内部遵循这个原则,其中 `T` 的删除是 `Object`,但不在主方法内,其中 `T` 已经绑定到 `String`,但是数组的指定类型仍然是 `Object`。 -在我们看到如何根据这个原则创建数组之前,还有一点值得强调。回想一下,泛型为 `Java` 伴随着 `cast-iro` 保证:只要没有未经检查的警告,通过擦除插入的转换将不会失败。上述原则说明了相反的情况:如果有未经检查的警告,则通过擦除插入的演员可能会失败。此外,失败的演员可能会在源代码的不同部分,而不是负责未经检查的警告!这就是为什么生成未经检查的警告的代码必须非常谨慎地编写。 +在我们看到如何根据这个原则创建数组之前,还有一点值得强调。回想一下,泛型为 `Java` 伴随着 `cast-iro` 保证:只要没有未经检查的警告,通过擦除插入的转换 +将不会失败。上述原则说明了相反的情况:如果有未经检查的警告,则通过擦除插入的演员可能会失败。此外,失败的演员可能会在源代码的不同部分,而不是负责未经 +检查的警告!这就是为什么生成未经检查的警告的代码必须非常谨慎地编写。 -**数组生成数组** 1732年托马斯富勒说,“这是赚钱的钱”,他注意到赚钱的一种方式是已经有钱。同样,获取一个通用类型的新数组的一种方法是已经有一个这种类型的数组。然后,可以从旧的数组中复制新数组的特定类型信息。 +**数组生成数组** 1732年托马斯富勒说,“这是赚钱的钱”,他注意到赚钱的一种方式是已经有钱。同样,获取一个通用类型的新数组的一种方法是已经有一个这种类型 +的数组。然后,可以从旧的数组中复制新数组的特定类型信息。 -因此,我们改变了以前的方法来取两个参数,一个集合和一个数组。如果数组足够大以容纳集合,则集合将被复制到数组中。否则,将使用反射来分配与旧的相同通用类型的新数组,然后将该集合复制到新数组中。 +因此,我们改变了以前的方法来取两个参数,一个集合和一个数组。如果数组足够大以容纳集合,则集合将被复制到数组中。否则,将使用反射来分配与旧的相同通用类 +型的新数组,然后将该集合复制到新数组中。 以下是实施替代方案的代码: @@ -95,15 +108,21 @@ } ``` -这使用反射库中的三个方法来分配一个与旧数组具有相同组件类型的新数组:方法 `getClass`(在 `java.lang.Object` 中)返回表示数组类型的 `Class` 对象 `T []` ;方法 `getComponentType`(来自 `java.lang.Class`)返回表示数组的组件类型 `T` 的第二个 `Class` 对象; `newInstance`(在 `java.lang.reflect.Array` 中)方法分配一个具有给定组件类型和大小的新数组,同样是 `T []` 类型。调用 `newInstance` 的结果类型是 `Object`,因此需要使用未经检查的强制转换将结果转换为正确的类型 `T []`。 +这使用反射库中的三个方法来分配一个与旧数组具有相同组件类型的新数组:方法 `getClass`(在 `java.lang.Object` 中)返回表示数组类型的 `Class` 对象 +`T []` ;方法 `getComponentType`(来自 `java.lang.Class`)返回表示数组的组件类型 `T` 的第二个 `Class` 对象; `newInstance`(在 +`java.lang.reflect.Array` 中)方法分配一个具有给定组件类型和大小的新数组,同样是 `T []` 类型。调用 `newInstance` 的结果类型是 `Object`,因此需要 +使用未经检查的强制转换将结果转换为正确的类型 `T []`。 在 `Java 5` 中,类 `Class` 已更新为泛型类 `Class`;稍后更多。 -(一个微妙的观点:在调用 `newInstance` 时,为什么是结果类型 `Object` 而不是 `Object []`?因为通常 `newInstance` 可能会返回一个基本类型的数组,例如 `int []`,它是 `Object` 的子类型,但而不是 `Object []`。但是,这里不会发生,因为类型变量 `T` 必须代表引用类型。) +(一个微妙的观点:在调用 `newInstance` 时,为什么是结果类型 `Object` 而不是 `Object []`?因为通常 `newInstance` 可能会返回一个基本类型的数组,例 +如 `int []`,它是 `Object` 的子类型,但而不是 `Object []`。但是,这里不会发生,因为类型变量 `T` 必须代表引用类型。) 新数组的大小被视为给定集合的大小。如果旧数组的大小足以容纳集合,并且剩余空间,则在集合之后立即写入 `null` 以标记其结尾。 -测试代码创建一个长度为 `2` 的字符串列表,然后执行两个演示调用。既没有遇到前面描述的问题,因为根据广告中的真理原理,返回的数组具有指定类型 `String []`。第一个调用传递一个长度为零的数组,因此该列表被复制到一个新分配的数组中长度二。第二次调用传递一个长度为四的数组,所以列表被复制到现有数组中,并且在末尾写入一个空值; `null` 之后的原始数组内容不受影响。实用方法 `toString`(在 `java.util.Arrays` 中)用于将数组转换为断言中的字符串。 +测试代码创建一个长度为 `2` 的字符串列表,然后执行两个演示调用。既没有遇到前面描述的问题,因为根据广告中的真理原理,返回的数组具有指定类型 +`String []`。第一个调用传递一个长度为零的数组,因此该列表被复制到一个新分配的数组中长度二。第二次调用传递一个长度为四的数组,所以列表被复制到现有数 +组中,并且在末尾写入一个空值; `null` 之后的原始数组内容不受影响。实用方法 `toString`(在 `java.util.Arrays` 中)用于将数组转换为断言中的字符串。 集合框架包含两个将集合转换为数组的方法,类似于我们刚刚讨论的那个: @@ -115,17 +134,23 @@ } ``` -第一个方法返回一个带有指定组件类型 `Object` 的数组,而第二个方法从参数数组中复制指定组件类型,就像上面的静态方法一样。 就像那种方法一样,如果有空间的话,它会把集合复制到数组中(如果有空间的话,它会在集合的末尾写入一个空值),否则就分配一个新的数组。 对第一个方法 `c.toArray()` 的调用返回与使用空对象 `c.toArray(new Object [0])` 的第二个方法调用相同的结果。 这些方法将在第 `12` 章开头进一步讨论。 +第一个方法返回一个带有指定组件类型 `Object` 的数组,而第二个方法从参数数组中复制指定组件类型,就像上面的静态方法一样。 就像那种方法一样,如果有空间 +的话,它会把集合复制到数组中(如果有空间的话,它会在集合的末尾写入一个空值),否则就分配一个新的数组。 对第一个方法 `c.toArray()` 的调用返回与使用空 +对象 `c.toArray(new Object [0])` 的第二个方法调用相同的结果。 这些方法将在第 `12` 章开头进一步讨论。 -通常在遇到这种设计时,程序员认为数组参数主要出于效率原因存在,以便通过重新使用数组来最小化分配。 这确实是设计的一个好处,但它的主要目的是让指定类型正确! 大多数对 `toArray` 的调用将使用长度为零的参数数组。 +通常在遇到这种设计时,程序员认为数组参数主要出于效率原因存在,以便通过重新使用数组来最小化分配。 这确实是设计的一个好处,但它的主要目的是让指定类型正 +确! 大多数对 `toArray` 的调用将使用长度为零的参数数组。 **一个优雅的选择** 有些时候,看起来赚钱的唯一方式就是赚钱。数组并不完全一样。使用数组创建数组的另一种方法是使用类Class的实例。 类的实例在运行时表示关于类的信息;也有这个类的实例代表原始类型和数组。在本文中,我们将把 `Class` 类的实例称为类标记。 -在 `Java 5` 中,类 `Class` 已经变得通用,现在具有 `Class` 的形式。 `T` 代表什么? `Class` 类型的实例表示类型 `T`.例如,`String.class` 的类型为 `Class`。 +在 `Java 5` 中,类 `Class` 已经变得通用,现在具有 `Class` 的形式。 `T` 代表什么? `Class` 类型的实例表示类型 `T`.例如,`String.class` 的 +类型为 `Class`。 -我们可以定义一个我们以前方法的变体,它接受类型为 `Class` 的类标记而不是类型为 `T []` 的数组。将 `newInstance` 应用于类型为 `Class` 的类标记将返回一个类型为 `T []` 的新数组,其类型标记将指定组件类型。 `newInstance` 方法仍然具有 `Object` 的返回类型(因为与原始数组相同的问题),所以仍然需要未经检查的转换。 +我们可以定义一个我们以前方法的变体,它接受类型为 `Class` 的类标记而不是类型为 `T []` 的数组。将 `newInstance` 应用于类型为 `Class` 的类标记 +将返回一个类型为 `T []` 的新数组,其类型标记将指定组件类型。 `newInstance` 方法仍然具有 `Object` 的返回类型(因为与原始数组相同的问题),所以仍然 +需要未经检查的转换。 ```java import java.util.*; @@ -148,139 +173,5 @@ `Class` 类型表示泛型的一个有趣用法,与集合或比较器完全不同。 如果您仍然觉得这种泛型使用混淆,请不要担心 - 我们将在第 `7` 章中详细介绍这个主题。 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +《《《 [下一节](06_The_Principle_of_Indecent_Exposure.md)
+《《《 [返回首页](../README.md) From d9de58c376ce963ab04511e56a893ce81c0a3bd3 Mon Sep 17 00:00:00 2001 From: maskleo Date: Sun, 15 Apr 2018 18:33:50 +0800 Subject: [PATCH 17/21] Update 06_The_Principle_of_Indecent_Exposure.md --- ch06/06_The_Principle_of_Indecent_Exposure.md | 118 ++++-------------- 1 file changed, 21 insertions(+), 97 deletions(-) diff --git a/ch06/06_The_Principle_of_Indecent_Exposure.md b/ch06/06_The_Principle_of_Indecent_Exposure.md index 69be6edf..42a1c8e2 100644 --- a/ch06/06_The_Principle_of_Indecent_Exposure.md +++ b/ch06/06_The_Principle_of_Indecent_Exposure.md @@ -1,6 +1,10 @@ +《《《 [返回首页](../README.md)
+《《《 [上一节](05_The_Principle_of_Truth_in_Advertising.md) + ## 不雅暴露的原则 -尽管创建一个不可赋予的组件类型的数组是错误的,但可以声明一个具有这种类型的数组,并对此类型执行未经检查的转换。 必须谨慎使用这些功能,如果使用不当,有必要了解可能出现的问题。 特别是,一个库不应公开公开一个具有不可确定类型的数组。 +尽管创建一个不可赋予的组件类型的数组是错误的,但可以声明一个具有这种类型的数组,并对此类型执行未经检查的转换。 必须谨慎使用这些功能,如果使用不当,有 +必要了解可能出现的问题。 特别是,一个库不应公开公开一个具有不可确定类型的数组。 回想一下,`2.5` 节介绍了为什么需要物化的一个例子: @@ -11,7 +15,8 @@ int n = ints[0]; ``` -这将整数数组赋给一个数组数组,然后尝试将一个 `double` 存储到数组数组中。 该尝试引发数组存储异常,因为该检查与实体类型有关。 这也是一样,因为否则最后一行会尝试将 `double` 存储到整数变量中。 +这将整数数组赋给一个数组数组,然后尝试将一个 `double` 存储到数组数组中。 该尝试引发数组存储异常,因为该检查与实体类型有关。 这也是一样,因为否则最后 +一行会尝试将 `double` 存储到整数变量中。 下面是一个类似的例子,数组数组被数组列表所取代: @@ -23,7 +28,8 @@ int n = intLists[0].get(0); // 类抛出异常! ``` -这将整数列表分配给数组列表,然后尝试将双列表存储到数组列表中。 这次尝试的存储不会失败,即使它应该,因为针对被指定类型的检查是不充分的:被指定的信息只包含删除类型,表示它是一个 `List` 数组,而不是一个 `List`。因此,商店成功,程序意外地在别处失败。 +这将整数列表分配给数组列表,然后尝试将双列表存储到数组列表中。 这次尝试的存储不会失败,即使它应该,因为针对被指定类型的检查是不充分的:被指定的信息只 +包含删除类型,表示它是一个 `List` 数组,而不是一个 `List`。因此,商店成功,程序意外地在别处失败。 例 `6-1`。 避免不可接受类型的数组 @@ -52,7 +58,9 @@ } ``` -例 `6-1` 给出了一个类似的例子,分为两类,以说明设计不佳的图书馆如何为无辜的客户创造问题。 名为 `DeceptiveLibrary` 的第一个类定义了一个静态方法,该方法返回给定大小的整数列表数组。 由于不允许通用数组的创建,因此使用原始类型 `List` 的组件创建数组,并使用强制类型为组件提供参数化类型 `List`。 演员阵容会产生一个未经检查的警告: +例 `6-1` 给出了一个类似的例子,分为两类,以说明设计不佳的图书馆如何为无辜的客户创造问题。 名为 `DeceptiveLibrary` 的第一个类定义了一个静态方法,该 +方法返回给定大小的整数列表数组。 由于不允许通用数组的创建,因此使用原始类型 `List` 的组件创建数组,并使用强制类型为组件提供参数化类型 +`List`。 演员阵容会产生一个未经检查的警告: ```java %javac -Xlint:unchecked DeceptiveLibrary.java @@ -66,7 +74,8 @@ 由于该数组确实是一个整数列表数组,因此该数组似乎是合理的,并且您可能认为可以安全地忽略此警告。 正如我们将要看到的,你无视这个警告! -第二个类叫做 `InnocentClient` ,它有一个类似于前面例子的主要方法。 由于未检查的强制转换出现在库中,因此在编译此代码时不会发出未经检查的警告。 但是,运行代码会用双精度列表覆盖整数列表。 尝试从整数列表中提取整数会导致通过擦除隐式插入的强制转换失败: +第二个类叫做 `InnocentClient` ,它有一个类似于前面例子的主要方法。 由于未检查的强制转换出现在库中,因此在编译此代码时不会发出未经检查的警告。 但是, +运行代码会用双精度列表覆盖整数列表。 尝试从整数列表中提取整数会导致通过擦除隐式插入的强制转换失败: ```java %java InnocentClient @@ -80,7 +89,8 @@ > 从不公开暴露一个阵列,其中组件不具有可调整类型。 -再次,这是一种情况,在程序的一部分中未经检查的转换可能导致在完全不同的部分发生类转换错误,其中转换不会出现在源代码中,而是通过擦除引入。由于此类错误可能会非常混乱,因此必须谨慎使用未经检查的演员表。 +再次,这是一种情况,在程序的一部分中未经检查的转换可能导致在完全不同的部分发生类转换错误,其中转换不会出现在源代码中,而是通过擦除引入。由于此类错误 +可能会非常混乱,因此必须谨慎使用未经检查的演员表。 广告真相原则与不雅暴露原则密切相关。第一个要求数组的运行时类型被适当地赋值,第二个要求数组的编译时类型必须是可赋值的。 @@ -91,97 +101,11 @@ TypeVariable[] java.lang.Reflect.Method.getTypeParameters() ``` -遵循前面的模型,创建自己的Innocent Client版本并不难,它会在没有投射的地方抛出类抛出错误,在这种情况下,正式Java库会播放 `DeceptiveLibrary` 的角色! (在出版时,这个错误的补救措施正在考虑之中,可能的解决方法是从 `TypeVariable` 中删除类型参数,以便这些方法返回一个指定类型的数组,或者用列表替换数组。 +遵循前面的模型,创建自己的Innocent Client版本并不难,它会在没有投射的地方抛出类抛出错误,在这种情况下,正式Java库会播放 `DeceptiveLibrary` 的角 +色! (在出版时,这个错误的补救措施正在考虑之中,可能的解决方法是从 `TypeVariable` 中删除类型参数,以便这些方法返回一个指定类型的数组,或者用列表替 +换数组。 不要以同样的方式被抓到 - 一定要严格遵循恶意曝光原则在您自己的代码中! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +《《《 [下一节](07_How_to_Define_ArrayList.md)
+《《《 [返回首页](../README.md) From bade58a73f9228ff39445b5902d7ac82f6dfcea8 Mon Sep 17 00:00:00 2001 From: maskleo Date: Sun, 15 Apr 2018 18:35:15 +0800 Subject: [PATCH 18/21] Update 07_How_to_Define_ArrayList.md --- ch06/07_How_to_Define_ArrayList.md | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/ch06/07_How_to_Define_ArrayList.md b/ch06/07_How_to_Define_ArrayList.md index 67899386..d4daf1b0 100644 --- a/ch06/07_How_to_Define_ArrayList.md +++ b/ch06/07_How_to_Define_ArrayList.md @@ -1,15 +1,31 @@ +《《《 [返回首页](../README.md)
+《《《 [上一节](06_The_Principle_of_Indecent_Exposure.md) + ## 如何定义 ArrayList -我们在其他地方争辩说,通常最好使用列表而不是使用数组。有一些地方这不合适。在极少数情况下,出于效率或兼容性的原因,您将需要使用数组。另外,当然,你需要使用数组来实现 `ArrayList` 本身。在这里,我们将 `ArrayList` 的实现作为一种模型,在极少情况下需要使用数组。这些实现需要谨慎编写,因为它们必然涉及使用未经检查的强制转换。我们将看到实施中的“不雅暴露原则”和“广告真相原理”。 +我们在其他地方争辩说,通常最好使用列表而不是使用数组。有一些地方这不合适。在极少数情况下,出于效率或兼容性的原因,您将需要使用数组。另外,当然,你需要使 +用数组来实现 `ArrayList` 本身。在这里,我们将 `ArrayList` 的实现作为一种模型,在极少情况下需要使用数组。这些实现需要谨慎编写,因为它们必然涉及使用未经 +检查的强制转换。我们将看到实施中的“不雅暴露原则”和“广告真相原理”。 -例 `6-2` 显示了实现。我们通过从 `AbstractList` 中继承派生了 `ArrayList`。从这个类派生的类只需要定义四个方法,即 `get`,`set`,`add` 和 `remove`;其他方法是根据这些定义的。我们还指出类实现了 `RandomAccess`,表明类的客户端使用 `get` 比使用列表迭代器更有效。 +例 `6-2` 显示了实现。我们通过从 `AbstractList` 中继承派生了 `ArrayList`。从这个类派生的类只需要定义四个方法,即 `get`,`set`,`add` 和 `remove`;其 +他方法是根据这些定义的。我们还指出类实现了 `RandomAccess`,表明类的客户端使用 `get` 比使用列表迭代器更有效。 + +该类用两个私有字段表示包含 `E` 类型元素的列表:包含列表长度的 `int` 类型大小,以及包含列表元素的类型为 `E []` 的 `arr`。该数组的长度必须至少等于大小, +但最后可能会有其他未使用的元素。 -该类用两个私有字段表示包含E类型元素的列表:包含列表长度的 `int` 类型大小,以及包含列表元素的类型为 `E []` 的 `arr`。该数组的长度必须至少等于大小,但最后可能会有其他未使用的元素。 +有两个地方分配数组的新实例,一个在类的初始化器中,另一个在增加数组容量的方法中(这又从 `add` 方法中调用)。在这两个地方,数组都被分配为一个 +`Object []`,并且未勾选的类型转换为 `E []`。 -有两个地方分配数组的新实例,一个在类的初始化器中,另一个在增加数组容量的方法中(这又从 `add` 方法中调用)。在这两个地方,数组都被分配为一个 `Object []`,并且未勾选的类型转换为 `E []`。 +包含数组的字段是私人的是非常重要的;否则将违反广告真理原则和不雅暴露原则。这违反了广告中的真理原则,因为 `E` 可能被绑定到 `Object` 以外的类型(如 +`String`)。这会违反不雅暴露原则,因为 `E` 可能会绑定到不是可保留类型的类型(例如 `List`)。但是,这些原则都没有违反,因为该数组并非公开的: +它存储在私人领域,没有指向数组的指针从类中逃脱。我们可以称之为封闭门背后的任何原则。 -包含数组的字段是私人的是非常重要的;否则将违反广告真理原则和不雅暴露原则。这违反了广告中的真理原则,因为 `E` 可能被绑定到 `Object` 以外的类型(如 `String`)。这会违反不雅暴露原则,因为 `E` 可能会绑定到不是可保留类型的类型(例如 `List`)。但是,这些原则都没有违反,因为该数组并非公开的:它存储在私人领域,没有指向数组的指针从类中逃脱。我们可以称之为封闭门背后的任何原则。 +我们在这里定义 `ArrayList` 的方式接近 `Sun` 发布的源代码中的实际定义。最近,该库的共同作者 `Neal Gafter` 认为他使用了糟糕的风格 - 如果声明私有数组的 +类型为 `Object []`,并且在从数组中检索元素时使用强制类型(`E`)会更好。对于这一点,有些话要说,尽管对于我们在这里使用的风格也有一些要说的,这可以最大限 +度地减少对未经检查的演员的需求。 -我们在这里定义 `ArrayList` 的方式接近 `Sun` 发布的源代码中的实际定义。最近,该库的共同作者 `Neal Gafter` 认为他使用了糟糕的风格 - 如果声明私有数组的类型为 `Object []`,并且在从数组中检索元素时使用强制类型(`E`)会更好。对于这一点,有些话要说,尽管对于我们在这里使用的风格也有一些要说的,这可以最大限度地减少对未经检查的演员的需求。 +`toArray` 的方法确实会公开返回一个数组,但它使用了第 `6.5` 节中所述的技巧,依照广告中的真理原则。和之前一样,有一个参数数组,如果它不足以容纳集合,则使 +用反射来分配具有相同指定类型的新数组。这个实现类似于我们前面看到的实现,除了可以使用更高效的 `arraycopy` 例程将私有数组复制到公用数组中以返回。 -`toArray` 的方法确实会公开返回一个数组,但它使用了第 `6.5` 节中所述的技巧,依照广告中的真理原则。和之前一样,有一个参数数组,如果它不足以容纳集合,则使用反射来分配具有相同指定类型的新数组。这个实现类似于我们前面看到的实现,除了可以使用更高效的 `arraycopy` 例程将私有数组复制到公用数组中以返回。 \ No newline at end of file +《《《 [下一节](08_Array_Creation_and_Varargs.md)
+《《《 [返回首页](../README.md) From a607a608787956ad035faea659380483b2774219 Mon Sep 17 00:00:00 2001 From: maskleo Date: Sun, 15 Apr 2018 18:36:50 +0800 Subject: [PATCH 19/21] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=B8=8A=E4=B8=8B?= =?UTF-8?q?=E5=85=B3=E8=81=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ch06/08_Array_Creation_and_Varargs.md | 100 ++++---------------------- 1 file changed, 14 insertions(+), 86 deletions(-) diff --git a/ch06/08_Array_Creation_and_Varargs.md b/ch06/08_Array_Creation_and_Varargs.md index 68d59e4b..6f2bfbc2 100644 --- a/ch06/08_Array_Creation_and_Varargs.md +++ b/ch06/08_Array_Creation_and_Varargs.md @@ -1,6 +1,10 @@ +《《《 [返回首页](../README.md)
+《《《 [上一节](07_How_to_Define_ArrayList.md) + ## 数组创建和可变参数 -方便的变量表示法允许方法接受可变数量的参数并将它们打包到数组中,如 `1.4` 节所述。 这种表示法并不像你想要的那样方便,因为它创建的数组受到与其他数组相同的涉及到物化的问题的困扰。 +方便的变量表示法允许方法接受可变数量的参数并将它们打包到数组中,如 `1.4` 节所述。 这种表示法并不像你想要的那样方便,因为它创建的数组受到与其他数组相 +同的涉及到物化的问题的困扰。 例 `6-2`。 如何定义 `ArrayList` @@ -96,91 +100,15 @@ 这也会产生警告,出于同样的原因可能会造成混淆。 -通常,通用阵列创建报告错误。作为一种解决方法,可以创建一个可修饰类型的数组,并执行未经检查的转换。该变通方法不适用于可变参数使用中隐含的数组创建,因此在这种情况下,通用数组创建会发出警告而不是错误。通用数组创建警告就像未经检查的警告一样,因为它会使伴随泛型的铸铁保证失效。将前面的每个示例都作为未经检查的警告的结果,并不难,并创建一个使用可变参数的类似示例,其中发出通用数组创建警告。 - -在我们看来,可变参数提供的便利性超过了未经检查的警告中固有的危险,并且我们建议您在参数不可接受时避免使用可变参数。例如,在前面的两个例子中,我们不是使用 `Arrays.asList`,而是创建一个新的 `ArrayList` 并使用 `add`方法,即使这样做不太方便也不那么高效。 - -如果可变参数符号已被定义为将参数打包到列表而不是数组中,那么就不会出现通用数组创建警告和相关变通方法的需要,因为 `T ...` 等同于 `List` 而不是 `T[]`。不幸的是,可变参数表示法是在完全理解这个问题之前设计的。 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +通常,通用阵列创建报告错误。作为一种解决方法,可以创建一个可修饰类型的数组,并执行未经检查的转换。该变通方法不适用于可变参数使用中隐含的数组创建,因 +此在这种情况下,通用数组创建会发出警告而不是错误。通用数组创建警告就像未经检查的警告一样,因为它会使伴随泛型的铸铁保证失效。将前面的每个示例都作为未 +经检查的警告的结果,并不难,并创建一个使用可变参数的类似示例,其中发出通用数组创建警告。 +在我们看来,可变参数提供的便利性超过了未经检查的警告中固有的危险,并且我们建议您在参数不可接受时避免使用可变参数。例如,在前面的两个例子中,我们不是 +使用 `Arrays.asList`,而是创建一个新的 `ArrayList` 并使用 `add`方法,即使这样做不太方便也不那么高效。 +如果可变参数符号已被定义为将参数打包到列表而不是数组中,那么就不会出现通用数组创建警告和相关变通方法的需要,因为 `T ...` 等同于 `List` 而不是 +`T[]`。不幸的是,可变参数表示法是在完全理解这个问题之前设计的。 +《《《 [下一节](09_Arrays_as_a_Deprecated_Type.md)
+《《《 [返回首页](../README.md) From da25e56d9ccea538d43ebe574052294acf6c79f9 Mon Sep 17 00:00:00 2001 From: maskleo Date: Sun, 15 Apr 2018 18:39:51 +0800 Subject: [PATCH 20/21] Update 09_Arrays_as_a_Deprecated_Type.md --- ch06/09_Arrays_as_a_Deprecated_Type.md | 110 +++++++++++-------------- 1 file changed, 49 insertions(+), 61 deletions(-) diff --git a/ch06/09_Arrays_as_a_Deprecated_Type.md b/ch06/09_Arrays_as_a_Deprecated_Type.md index ee0c6d41..a4b00750 100644 --- a/ch06/09_Arrays_as_a_Deprecated_Type.md +++ b/ch06/09_Arrays_as_a_Deprecated_Type.md @@ -1,16 +1,25 @@ +《《《 [返回首页](../README.md)
+《《《 [上一节](08_Array_Creation_and_Varargs.md) + ## 作为已弃用类型的阵列? 我们已经看到,在许多方面集合优于数组: - - 集合比数组提供更精确的输入。 通过列表,可以编写 `List`,`List ` 或 `List `; 而对于数组,只能写 `T []`,这对应于列表的三个选项中的第二个。 更精确的打字可以在编译时检测到更多的错误,而不是运行时。 这使编码,调试,测试和维护更容易,并且还提高了效率。 (见 `2.5` 节) + - 集合比数组提供更精确的输入。 通过列表,可以编写 `List`,`List ` 或 `List `; 而对于数组,只能写 `T []`,这对应于 +列表的三个选项中的第二个。 更精确的打字可以在编译时检测到更多的错误,而不是运行时。 这使编码,调试,测试和维护更容易,并且还提高了效率。 (见 `2.5` +节) - - 集合比数组更灵活。 集合提供了各种表示形式,包括数组,链表,树和散列表,而数组具有固定的表示形式,这些库为集合提供了比数组更多的方法和便利算法。 (见 `2.5` 节) + - 集合比数组更灵活。 集合提供了各种表示形式,包括数组,链表,树和散列表,而数组具有固定的表示形式,这些库为集合提供了比数组更多的方法和便利算法。 +(见 `2.5` 节) - - 集合可能具有任何类型的元素,而数组只能具有可定义类型的组件。 在创建数组时,必须遵守广告中的真理原则 - 具体化类型必须符合静态类型 - 以及不雅暴露原则 - 从不公开暴露组件不具有可确定类型的数组。 (见第 `6.5` 节和第 `6.6` 节)。 + - 集合可能具有任何类型的元素,而数组只能具有可定义类型的组件。 在创建数组时,必须遵守广告中的真理原则 - 具体化类型必须符合静态类型 - 以及不雅暴露 +原则 - 从不公开暴露组件不具有可确定类型的数组。 (见第 `6.5` 节和第 `6.6` 节)。 回想起来,在Java 5中有几个地方避免使用数组可能会改进设计: - - 变长参数(可变参数)由一个数组表示,因此受到相同的限制。 如果可变参数绑定到不可确定类型的实参,则会发出一个通用数组创建警告(这引发了与未检查警告相同的担忧)。 例如,函数 `Arrays.asList` 需要一个可变参数。 使用此函数返回 `List` 类型的结果没有任何困难,但创建类型为 `List>` 或类型为 `List ` 的结果存在问题。 如果列表优先于数组,则不会出现此问题。 (见 `6.8` 节) + - 变长参数(可变参数)由一个数组表示,因此受到相同的限制。 如果可变参数绑定到不可确定类型的实参,则会发出一个通用数组创建警告(这引发了与未检查警 +告相同的担忧)。 例如,函数 `Arrays.asList` 需要一个可变参数。 使用此函数返回 `List` 类型的结果没有任何困难,但创建类型为 +`List>` 或类型为 `List ` 的结果存在问题。 如果列表优先于数组,则不会出现此问题。 (见 `6.8` 节) - Java库中的某些方法的签名违反了不雅曝光原则: @@ -19,11 +28,14 @@ TypeVariable[] java.lang.Reflect.Method.getTypeParameters() ``` - 调用这些方法的代码有可能违反伴随泛型的铸铁保证:即使编译器没有发出未经检查的警告,它也可能在代码中没有显式强制转换的情况下引发类转换异常。 (编译库时发出了警告 - 错误地忽略了)。同样,如果列表优先于数组,则不会出现此问题。 (见 `6.6` 节) +调用这些方法的代码有可能违反伴随泛型的铸铁保证:即使编译器没有发出未经检查的警告,它也可能在代码中没有显式强制转换的情况下引发类转换异常。 (编译库时 +发出了警告 - 错误地忽略了)。同样,如果列表优先于数组,则不会出现此问题。 (见 `6.6` 节) `Java 5` 设计中一些复杂性的一个原因是为使用数组提供了很好的支持。 回想起来,选择更简单的设计可能会更好,但是使得数组的使用并不方便: - - 数组必须使用可定义类型的组件创建,因此为尽量减少这种限制,尝试尽可能地使可保存类型的概念成为一般。 如果设计人员愿意限制实现类型的概念,他们可以通过包含原始类型(如 `List`)来简化它,但不包括带有无限通配符的类型(如 `List`)。 如果他们这样做了,可重用类型就会成为未参数化类型的代名词(即原始类型,原始类型和没有类型参数的类型)。这种变化将简化实例测试中允许的类型。 考虑以下三个测试: + - 数组必须使用可定义类型的组件创建,因此为尽量减少这种限制,尝试尽可能地使可保存类型的概念成为一般。 如果设计人员愿意限制实现类型的概念,他们可以 +通过包含原始类型(如 `List`)来简化它,但不包括带有无限通配符的类型(如 `List`)。 如果他们这样做了,可重用类型就会成为未参数化类型的代名词(即原 +始类型,原始类型和没有类型参数的类型)。这种变化将简化实例测试中允许的类型。 考虑以下三个测试: ```java obj instanceof List @@ -31,68 +43,44 @@ obj instanceof List ``` - 目前,前两个是允许的,但第三个不是。 通过限制建议,只允许第一个限制。 可以说,这可能更容易理解。 这也符合对类标记的处理,因为目前允许 `List.class`,但 `List .class` 是非法的。 +目前,前两个是允许的,但第三个不是。 通过限制建议,只允许第一个限制。 可以说,这可能更容易理解。 这也符合对类标记的处理,因为目前允许 `List.class`, +但 `List .class` 是非法的。 - - 目前,数组创建仅限于可调整类型的数组。 但是允许声明一个不可确认类型的数组,或者将其转换为不可确定的数组类型,代价是在代码中的某处未经检查的警告。 正如我们所看到的,这样的警告违反了与泛型相伴随的铸铁保证,并且即使源代码不包含转换,也可能导致类转换错误。 - 一个更简单和更安全的设计将取缔任何不可接受类型的数组(使用刚才描述的更简单形式的可重写类型)。 这种设计意味着我们永远不能声明一个 `E []` 类型的数组,其中 `E` 是一个类型变量。 + - 目前,数组创建仅限于可调整类型的数组。 但是允许声明一个不可确认类型的数组,或者将其转换为不可确定的数组类型,代价是在代码中的某处未经检查的警 +告。 正如我们所看到的,这样的警告违反了与泛型相伴随的铸铁保证,并且即使源代码不包含转换,也可能导致类转换错误。 + +一个更简单和更安全的设计将取缔任何不可接受类型的数组(使用刚才描述的更简单形式的可重写类型)。 这种设计意味着我们永远不能声明一个 `E []` 类型的数组, +其中 `E` 是一个类型变量。 - 这种改变会让实现 `ArrayList`(或类似的类)变得更加复杂。 私有变量的类型必须从 `E []` 更改为 `Object []`,并且必须将未经检查的强制类型(`E`)添加到 `get` 和类似方法的结果中。 但是复杂性很小,并且仅出现在 `ArrayList`(或类似类)的实现者中,而不是客户端。 +这种改变会让实现 `ArrayList`(或类似的类)变得更加复杂。 私有变量的类型必须从 `E []` 更改为 `Object []`,并且必须将未经检查的强制类型(`E`)添 +加到 `get` 和类似方法的结果中。 但是复杂性很小,并且仅出现在 `ArrayList`(或类似类)的实现者中,而不是客户端。 - 此更改也意味着您无法为集合(或类似方法)的 `toArray` 方法分配泛型类型。 代替: - - ```java - public T[] toArray(T[] arr) - ``` +此更改也意味着您无法为集合(或类似方法)的 `toArray` 方法分配泛型类型。 代替: - 我们会有: - - ```java - public Object[] toArray(Object[] arr) - ``` +```java + public T[] toArray(T[] arr) +``` + +我们会有: + +```java + public Object[] toArray(Object[] arr) +``` - 并且这种方法的许多用途都需要显式地输出结果。 这确实会让用户的生活变得更加尴尬,但可以说,简单性和安全性的提高是值得的。 +并且这种方法的许多用途都需要显式地输出结果。 这确实会让用户的生活变得更加尴尬,但可以说,简单性和安全性的提高是值得的。 - - 前面的更改意味着通常会使用优先于数组的列表。 通过允许 `Java` 程序员将 `l [i]` 编写为 `l.get(i)` 的缩写,并将 `l [i] = v` 作为 `l.put(i,v)` 的缩写,可以使列表的使用变得更容易。 (有些人喜欢这种“语法糖”,而有些人则认为它是“句法老鼠毒”。) + - 前面的更改意味着通常会使用优先于数组的列表。 通过允许 `Java` 程序员将 `l [i]` 编写为 `l.get(i)` 的缩写,并将 `l [i] = v` 作为 `l.put(i,v)` +的缩写,可以使列表的使用变得更容易。 (有些人喜欢这种“语法糖”,而有些人则认为它是“句法老鼠毒”。) -其中一些变化仍然可以以后向兼容的方式进行调整。我们在 `6.8` 节中提到,可能需要添加基于列表而不是数组的第二种可变参数形式。 允许使用缩写来使列表索引看起来像数组索引可以很容易地被合并到未来的 `Java` 版本中。 +其中一些变化仍然可以以后向兼容的方式进行调整。我们在 `6.8` 节中提到,可能需要添加基于列表而不是数组的第二种可变参数形式。 允许使用缩写来使列表索引看 +起来像数组索引可以很容易地被合并到未来的 `Java` 版本中。 -但是其中一些变化的窗口已经关闭。 太多用户使用通用 `toArrays` 编写代码以允许恢复到非通用版本。 尽管如此,记录这种替代设计似乎是值得的。 也许理解当前设计如何更简单可以带来更好的洞察力和更好的未来设计。 +但是其中一些变化的窗口已经关闭。 太多用户使用通用 `toArrays` 编写代码以允许恢复到非通用版本。 尽管如此,记录这种替代设计似乎是值得的。 也许理解当前 +设计如何更简单可以带来更好的洞察力和更好的未来设计。 -正如 `Java 5` 设计如果不太重视数组一样,它可能已经得到了改进,如果您使用集合和列表而不是阵列,那么您自己的代码设计可能会得到改进。 也许现在已经到了将数组视为已弃用类型的时候了? - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +正如 `Java 5` 设计如果不太重视数组一样,它可能已经得到了改进,如果您使用集合和列表而不是阵列,那么您自己的代码设计可能会得到改进。 也许现在已经到了 +将数组视为已弃用类型的时候了? - \ No newline at end of file +《《《 [下一节](10_Summing_Up.md)
+《《《 [返回首页](../README.md) + From ae603be3107542b3ab790527a0570d6828271440 Mon Sep 17 00:00:00 2001 From: maskleo Date: Sun, 15 Apr 2018 18:41:08 +0800 Subject: [PATCH 21/21] last --- ch06/10_Summing_Up.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ch06/10_Summing_Up.md b/ch06/10_Summing_Up.md index 3d1eab17..05ab2dc5 100644 --- a/ch06/10_Summing_Up.md +++ b/ch06/10_Summing_Up.md @@ -1,3 +1,6 @@ +《《《 [返回首页](../README.md)
+《《《 [上一节](09_Arrays_as_a_Deprecated_Type.md) + ## 加起来 我们通过给出需要或推荐可评估类型的地方的清单来得出结论。 @@ -7,9 +10,13 @@ - 扩展 `Throwable` 的类不能参数化。 - 数组实例创建必须处于可修饰类型。 - 数组的指定类型必须是其静态类型的删除子类型(请参阅广告中的真理原则),并且公开暴露的数组应该是可确定类型的(请参阅不雅曝光原则)。 - - 可变参数应该是可确定的类型。 (可变类型的变量将发出未经检查的警告。)这些限制来自泛型通过擦除来实现的事实,它们应该被视为我们在前一章探讨的易于进化的价格。 + - 可变参数应该是可确定的类型。 (可变类型的变量将发出未经检查的警告。)这些限制来自泛型通过擦除来实现的事实,它们应该被视为我们在前一章探讨的易于进化 +的价格。 为了完整性,我们还列出了与反射相关的限制: - 类令牌对应于可重用类型,`Class` 中的类型参数应该是可重用类型。 (见 `7.2` 节) -这些在下一章讨论。 \ No newline at end of file +这些在下一章讨论。 + +《《《 [下一节](../ch07/00_Reflection.md)
+《《《 [返回首页](../README.md)