135.md 6.1 KB
Newer Older
W
init  
wizardforcel 已提交
1 2 3 4
# 不可再生类型

> 原文: [https://docs.oracle.com/javase/tutorial/java/generics/nonReifiableVarargsType.html](https://docs.oracle.com/javase/tutorial/java/generics/nonReifiableVarargsType.html)

W
wizardforcel 已提交
5
[类型擦除](erasure.html)部分讨论了编译器删除与类型参数和类型参数相关的信息的过程。类型擦除具有与变量参数(也称为 _varargs_ )方法相关的后果,其 varargs 形式参数具有不可重新类型。有关 varargs 方法的更多信息,请参阅[将信息传递给方法或构造器](../javaOO/arguments.html)中的[任意参数数量](../javaOO/arguments.html#varargs)部分。
W
init  
wizardforcel 已提交
6 7 8 9 10 11 12 13

此页面包含以下主题:

*   [不可再生类型](#non-reifiable-types)
*   [堆污染](#heap_pollution)
*   [具有不可恢复形式参数的 Varargs 方法的潜在漏洞](#vulnerabilities)
*   [使用不可恢复的形式参数防止 Varargs 方法出现警告](#suppressing)

W
wizardforcel 已提交
14
*可再生*类型是一种类型信息在运行时完全可用的类型。这包括基元,非泛型类型,原始类型和未绑定通配符的调用。
W
init  
wizardforcel 已提交
15

W
wizardforcel 已提交
16
*不可再生类型*是在编译时通过类型擦除删除信息的类型 - 未定义为无界通配符的泛型类型的调用。不可重新生成的类型在运行时没有提供所有信息。不可再生类型的示例是`List< String>。``列表< Number>` ; JVM 无法在运行时区分这些类型。如[对泛型的限制](restrictions.html)所示,在某些情况下不能使用不可再生类型:例如,在`表达式`表达式中,或作为数组中的元素。
W
init  
wizardforcel 已提交
17

W
wizardforcel 已提交
18
*当参数化类型的变量引用不是该参数化类型的对象时,会发生堆污染*。如果程序执行某些操作,在编译时产生未经检查的警告,则会出现这种情况。如果在编译时(在编译时类型检查规则的限制范围内)或在运行时,生成*未检查警告*,则涉及参数化类型的操作的正确性(例如,强制转换)或方法调用)无法验证。例如,在混合原始类型和参数化类型时,或者在执行未经检查的强制转换时,会发生堆污染。
W
init  
wizardforcel 已提交
19 20 21 22 23

在正常情况下,当所有代码同时编译时,编译器会发出未经检查的警告,以引起您对潜在堆污染的注意。如果单独编译代码的各个部分,则很难检测到堆污染的潜在风险。如果确保代码在没有警告的情况下编译,则不会发生堆污染。

包含 vararg 输入参数的通用方法可能会导致堆污染。

W
wizardforcel 已提交
24
考虑以下`ArrayBuilder`类:
W
init  
wizardforcel 已提交
25

W
wizardforcel 已提交
26
```java
W
init  
wizardforcel 已提交
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
public class ArrayBuilder {

  public static <T> void addToList (List<T> listArg, T... elements) {
    for (T x : elements) {
      listArg.add(x);
    }
  }

  public static void faultyMethod(List<String>... l) {
    Object[] objectArray = l;     // Valid
    objectArray[0] = Arrays.asList(42);
    String s = l[0].get(0);       // ClassCastException thrown here
  }

}

```

W
wizardforcel 已提交
45
以下示例, `HeapPollutionExample`使用`ArrayBuiler`类:
W
init  
wizardforcel 已提交
46

W
wizardforcel 已提交
47
```java
W
init  
wizardforcel 已提交
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
public class HeapPollutionExample {

  public static void main(String[] args) {

    List<String> stringListA = new ArrayList<String>();
    List<String> stringListB = new ArrayList<String>();

    ArrayBuilder.addToList(stringListA, "Seven", "Eight", "Nine");
    ArrayBuilder.addToList(stringListB, "Ten", "Eleven", "Twelve");
    List<List<String>> listOfStringLists =
      new ArrayList<List<String>>();
    ArrayBuilder.addToList(listOfStringLists,
      stringListA, stringListB);

    ArrayBuilder.faultyMethod(Arrays.asList("Hello!"), Arrays.asList("World!"));
  }
}

```

W
wizardforcel 已提交
68
编译时, `ArrayBuilder.addToList`方法的定义会产生以下警告:
W
init  
wizardforcel 已提交
69

W
wizardforcel 已提交
70
```java
W
init  
wizardforcel 已提交
71 72 73 74 75 76 77 78
warning: [varargs] Possible heap pollution from parameterized vararg type T

```

当编译器遇到 varargs 方法时,它会将 varargs 形式参数转换为数组。但是,Java 编程语言不允许创建参数化类型的数组。在方法`ArrayBuilder.addToList`中,编译器将 varargs 形式参数`T... elements`转换为形式参数`T[] elements`。但是,由于类型擦除,编译器会将 varargs 形式参数转换为`Object[] elements`。因此,存在堆污染的可能性。

以下语句将 varargs 形式参数`l`分配给`Object`数组`objectArgs`

W
wizardforcel 已提交
79
```java
W
init  
wizardforcel 已提交
80 81 82 83 84 85 86 87
Object[] objectArray = l;

```

这种说法可能会引入堆污染。与 varargs 形式参数`l`的参数化类型匹配的值可以分配给变量`objectArray`,因此可以分配给`l`。但是,编译器不会在此语句中生成未经检查的警告。编译器在将 varargs 形式参数`List&lt;String&gt;... l`转换为形式参数`List[] l`时已生成警告。本声明有效;变量`l`的类型为`List[]`,它是`Object[]`的子类型。

因此,如果将任何类型的`List`对象分配给`objectArray`数组的任何数组组件,编译器不会发出警告或错误,如下所示:

W
wizardforcel 已提交
88
```java
W
init  
wizardforcel 已提交
89 90 91 92 93 94 95 96
objectArray[0] = Arrays.asList(42);

```

此语句使用`List`对象分配给`objectArray`数组的第一个数组组件,该对象包含一个`Integer`类型的对象。

假设您使用以下语句调用`ArrayBuilder.faultyMethod`

W
wizardforcel 已提交
97
```java
W
init  
wizardforcel 已提交
98 99 100 101 102 103
ArrayBuilder.faultyMethod(Arrays.asList("Hello!"), Arrays.asList("World!"));

```

在运行时,JVM 在以下语句处抛出`ClassCastException`

W
wizardforcel 已提交
104
```java
W
init  
wizardforcel 已提交
105 106 107 108 109
// ClassCastException thrown here
String s = l[0].get(0);

```

W
wizardforcel 已提交
110
存储在变量`l`的第一个数组组件中的对象具有`List&lt;Integer>`类型,但此语句期望`List&lt;String>`类型的对象。
W
init  
wizardforcel 已提交
111 112 113

如果声明具有参数化类型参数的 varargs 方法,并确保方法体不会因 varargs 形式参数处理不当而抛出`ClassCastException`或其他类似异常,则可以防止出现警告编译器通过向静态和非构造方法声明添加以下注释来为这些类型的 varargs 方法生成:

W
wizardforcel 已提交
114
```java
W
init  
wizardforcel 已提交
115 116 117 118 119 120 121 122
@SafeVarargs

```

`@SafeVarargs`注释是方法合同的记录部分;这个注释断言该方法的实现不会不正确地处理 varargs 形式参数。

尽管不太可取,但通过在方法声明中添加以下内容来抑制此类警告也是可能的:

W
wizardforcel 已提交
123
```java
W
init  
wizardforcel 已提交
124 125 126 127 128
@SuppressWarnings({"unchecked", "varargs"})

```

但是,此方法不会抑制从方法的调用站点生成的警告。如果您不熟悉`@SuppressWarnings`语法,请参阅[注释](../../java/annotations/index.html)