"Potential heap pollution via varargs parameter" 对于 Enum<E>... 为什么?

"Potential heap pollution via varargs parameter" for Enum<E>... why?

此问题特定于将可变参数与泛型 Enum<E>s 一起使用:

为什么我会收到此警告 Type safety: Potential heap pollution via varargs parameter elements 如果我这样定义方法:

<E extends Enum<E>> void someMethod(E... elements)

与此相反:

<E extends Enum<E>> void someMethod(E[] elements)

据此,声明方法前需要注意什么@SafeVarargs?

类似问题

这个问题类似于关于 Collection<T>... 的这些问题,但这些答案中显示的场景似乎不适用于 Enum<E>...:

反问为什么没有warning这个问题:

示例代码

这就是我试图污染堆的方法,但每次错误的尝试都会导致 java.lang.ArrayStoreException 而不是污染的数组。

我正在使用 Eclipse 4.6.0Java JDK 8u74.

public static void main(String[] args) {
    Foo[] x = { Foo.A };
    someMethod(x);

    Foo y = x[0];  // How does one get a ClassCastException here?
}

private static enum Foo {
    A, B, C,
}

private static enum Bar {
    X, Y, Z,
}

// This produces a "Type safety" warning on 'elements'
private static <E extends Enum<E>> void someMethod(E... elements) {
    Object[] objects = elements;

    // Test case 1: This line throws java.lang.ArrayStoreException
    objects[0] = "";

    // Test case 2: Program terminates without errors
    objects[0] = Foo.A;

    // Test case 3: This line throws java.lang.ArrayStoreException
    objects[0] = Bar.X;
}

可变参数方法有一个警告,因为可变参数方法可能导致在调用站点隐式创建数组,而采用数组参数的版本则不会。 varargs 的工作方式是它使编译器在调用站点创建一个数组,其中填充了可变参数,然后将其作为单个数组参数传递给方法。该参数的类型为 E[],因此创建的数组应为 E[].

首先,在您示例的调用站点中,您根本没有使用可变参数功能。您正在直接传递可变参数数组。所以在这种情况下没有隐式数组创建。

即使您使用了可变参数功能,例如使用 someMethod(Foo.A);,隐式数组创建将创建一个具有可具体化类型的数组,即变量参数类型在调用站点已知为 Foo,这是编译时已知的具体类型,所以数组创建没问题。

仅当调用站点的变量参数类型也是泛型类型或类型参数时才会出现问题。例如,类似于:

public static <E extends Enum<E>> void foo(E obj) {
    someMethod(obj);
}

然后编译器需要创建这个泛型类型或类型参数的数组(它需要创建一个 E[]),但是正如你所知,Java 中不允许创建泛型数组.它会创建一个数组,其组件类型是泛型类型的擦除(在这个例子中它将是 Enum),所以它会传递一个错误类型的数组(在这个例子中,传递的数组应该是 E[],但数组的实际运行时间 class 将是 Enum[],这不是 E[]).

的子类型

这种潜在的错误数组类型并不总是问题。大多数时候,可变参数方法将简单地遍历数组并从中获取元素。在这种情况下,数组的运行时间 class 是无关紧要的;重要的是元素是 E 类型(它们确实是)。如果是这种情况,您可以声明您的方法 @SafeVarargs。但是,如果您的方法实际使用传递数组的运行时 class(例如,如果您 return 可变参数数组类型为 E[],或者您使用 Arrays.copyOf()创建一个具有相同运行时 class 的数组,那么错误的运行时 class 数组将导致问题,您不能使用 @SafeVarargs.

您的示例有点奇怪,因为您甚至没有使用元素类型为 E 的事实,更不用说数组为 E[] 的事实了。所以你可以使用 @SafeVarargs,不仅如此,你可以简单地将数组声明为首先采用 Object[]

private static void someMethod(Object... objects)