# 不可再生类型

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

[类型擦除](erasure.html)部分讨论了编译器删除与类型参数和类型参数相关的信息的过程。类型擦除具有与变量参数（也称为 _varargs_ ）方法相关的后果，其 varargs 形式参数具有不可重新类型。有关 varargs 方法的更多信息，请参阅[将信息传递给方法或构造函数](../javaOO/arguments.html)中的[任意参数数量](../javaOO/arguments.html#varargs)部分。

此页面包含以下主题：

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

_ 可再生 _ 类型是一种类型信息在运行时完全可用的类型。这包括基元，非泛型类型，原始类型和未绑定通配符的调用。

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

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

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

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

考虑以下 `ArrayBuilder` 类：

```
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
  }

}

```

以下示例， `HeapPollutionExample` 使用 `ArrayBuiler` 类：

```
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!"));
  }
}

```

编译时， `ArrayBuilder.addToList` 方法的定义会产生以下警告：

```
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`：

```
Object[] objectArray = l;

```

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

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

```
objectArray[0] = Arrays.asList(42);

```

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

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

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

```

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

```
// ClassCastException thrown here
String s = l[0].get(0);

```

存储在变量`l`的第一个数组组件中的对象具有`List&lt;Integer&gt;`类型，但此语句期望`List&lt;String&gt;`类型的对象。

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

```
@SafeVarargs

```

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

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

```
@SuppressWarnings({"unchecked", "varargs"})

```

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