|
| 1 | +## 作为已弃用类型的阵列? |
| 2 | + |
| 3 | +我们已经看到,在许多方面集合优于数组: |
| 4 | + |
| 5 | + - 集合比数组提供更精确的输入。 通过列表,可以编写 `List<T>`,`List <? extends T>` 或 `List <? super T>`; 而对于数组,只能写 `T []`,这对应于列表的三个选项中的第二个。 更精确的打字可以在编译时检测到更多的错误,而不是运行时。 这使编码,调试,测试和维护更容易,并且还提高了效率。 (见 `2.5` 节) |
| 6 | + |
| 7 | + - 集合比数组更灵活。 集合提供了各种表示形式,包括数组,链表,树和散列表,而数组具有固定的表示形式,这些库为集合提供了比数组更多的方法和便利算法。 (见 `2.5` 节) |
| 8 | + |
| 9 | + - 集合可能具有任何类型的元素,而数组只能具有可定义类型的组件。 在创建数组时,必须遵守广告中的真理原则 - 具体化类型必须符合静态类型 - 以及不雅暴露原则 - 从不公开暴露组件不具有可确定类型的数组。 (见第 `6.5` 节和第 `6.6` 节)。 |
| 10 | + |
| 11 | +回想起来,在Java 5中有几个地方避免使用数组可能会改进设计: |
| 12 | + |
| 13 | + - 变长参数(可变参数)由一个数组表示,因此受到相同的限制。 如果可变参数绑定到不可确定类型的实参,则会发出一个通用数组创建警告(这引发了与未检查警告相同的担忧)。 例如,函数 `Arrays.asList` 需要一个可变参数。 使用此函数返回 `List<Integer>` 类型的结果没有任何困难,但创建类型为 `List<List <Integer >>` 或类型为 `List <E>` 的结果存在问题。 如果列表优先于数组,则不会出现此问题。 (见 `6.8` 节) |
| 14 | + |
| 15 | + - Java库中的某些方法的签名违反了不雅曝光原则: |
| 16 | + |
| 17 | + ```java |
| 18 | + TypeVariable<Class<T>>[] java.lang.Class.getTypeParameters() |
| 19 | + TypeVariable<Method>[] java.lang.Reflect.Method.getTypeParameters() |
| 20 | + ``` |
| 21 | + |
| 22 | + 调用这些方法的代码有可能违反伴随泛型的铸铁保证:即使编译器没有发出未经检查的警告,它也可能在代码中没有显式强制转换的情况下引发类转换异常。 (编译库时发出了警告 - 错误地忽略了)。同样,如果列表优先于数组,则不会出现此问题。 (见 `6.6` 节) |
| 23 | + |
| 24 | +`Java 5` 设计中一些复杂性的一个原因是为使用数组提供了很好的支持。 回想起来,选择更简单的设计可能会更好,但是使得数组的使用并不方便: |
| 25 | + |
| 26 | + - 数组必须使用可定义类型的组件创建,因此为尽量减少这种限制,尝试尽可能地使可保存类型的概念成为一般。 如果设计人员愿意限制实现类型的概念,他们可以通过包含原始类型(如 `List`)来简化它,但不包括带有无限通配符的类型(如 `List<?>`)。 如果他们这样做了,可重用类型就会成为未参数化类型的代名词(即原始类型,原始类型和没有类型参数的类型)。这种变化将简化实例测试中允许的类型。 考虑以下三个测试: |
| 27 | + |
| 28 | + ```java |
| 29 | + obj instanceof List |
| 30 | + obj instanceof List<?> |
| 31 | + obj instanceof List<? extends Object> |
| 32 | + ``` |
| 33 | + |
| 34 | + 目前,前两个是允许的,但第三个不是。 通过限制建议,只允许第一个限制。 可以说,这可能更容易理解。 这也符合对类标记的处理,因为目前允许 `List.class`,但 `List <?>.class` 是非法的。 |
| 35 | + |
| 36 | + - 目前,数组创建仅限于可调整类型的数组。 但是允许声明一个不可确认类型的数组,或者将其转换为不可确定的数组类型,代价是在代码中的某处未经检查的警告。 正如我们所看到的,这样的警告违反了与泛型相伴随的铸铁保证,并且即使源代码不包含转换,也可能导致类转换错误。 |
| 37 | + 一个更简单和更安全的设计将取缔任何不可接受类型的数组(使用刚才描述的更简单形式的可重写类型)。 这种设计意味着我们永远不能声明一个 `E []` 类型的数组,其中 `E` 是一个类型变量。 |
| 38 | + |
| 39 | + 这种改变会让实现 `ArrayList<E>`(或类似的类)变得更加复杂。 私有变量的类型必须从 `E []` 更改为 `Object []`,并且必须将未经检查的强制类型(`E`)添加到 `get` 和类似方法的结果中。 但是复杂性很小,并且仅出现在 `ArrayList`(或类似类)的实现者中,而不是客户端。 |
| 40 | + |
| 41 | + 此更改也意味着您无法为集合(或类似方法)的 `toArray` 方法分配泛型类型。 代替: |
| 42 | + |
| 43 | + ```java |
| 44 | + public <T> T[] toArray(T[] arr) |
| 45 | + ``` |
| 46 | + |
| 47 | + 我们会有: |
| 48 | + |
| 49 | + ```java |
| 50 | + public Object[] toArray(Object[] arr) |
| 51 | + ``` |
| 52 | + |
| 53 | + 并且这种方法的许多用途都需要显式地输出结果。 这确实会让用户的生活变得更加尴尬,但可以说,简单性和安全性的提高是值得的。 |
| 54 | + |
| 55 | + - 前面的更改意味着通常会使用优先于数组的列表。 通过允许 `Java` 程序员将 `l [i]` 编写为 `l.get(i)` 的缩写,并将 `l [i] = v` 作为 `l.put(i,v)` 的缩写,可以使列表的使用变得更容易。 (有些人喜欢这种“语法糖”,而有些人则认为它是“句法老鼠毒”。) |
| 56 | + |
| 57 | +其中一些变化仍然可以以后向兼容的方式进行调整。我们在 `6.8` 节中提到,可能需要添加基于列表而不是数组的第二种可变参数形式。 允许使用缩写来使列表索引看起来像数组索引可以很容易地被合并到未来的 `Java` 版本中。 |
| 58 | + |
| 59 | +但是其中一些变化的窗口已经关闭。 太多用户使用通用 `toArrays` 编写代码以允许恢复到非通用版本。 尽管如此,记录这种替代设计似乎是值得的。 也许理解当前设计如何更简单可以带来更好的洞察力和更好的未来设计。 |
| 60 | + |
| 61 | +正如 `Java 5` 设计如果不太重视数组一样,它可能已经得到了改进,如果您使用集合和列表而不是阵列,那么您自己的代码设计可能会得到改进。 也许现在已经到了将数组视为已弃用类型的时候了? |
| 62 | + |
| 63 | + |
| 64 | + |
| 65 | + |
| 66 | + |
| 67 | + |
| 68 | + |
| 69 | + |
| 70 | + |
| 71 | + |
| 72 | + |
| 73 | + |
| 74 | + |
| 75 | + |
| 76 | + |
| 77 | + |
| 78 | + |
| 79 | + |
| 80 | + |
| 81 | + |
| 82 | + |
| 83 | + |
| 84 | + |
| 85 | + |
| 86 | + |
| 87 | + |
| 88 | + |
| 89 | + |
| 90 | + |
| 91 | + |
| 92 | + |
| 93 | + |
| 94 | + |
| 95 | + |
| 96 | + |
| 97 | + |
| 98 | + |
0 commit comments