首页 文章

“通过varargs参数的潜在堆污染”为Enum <E> ...为什么?

提问于
浏览
7

This question is specific to using varargs with generic-fied 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>...

与此问题相反,它质疑为什么没有警告:

示例代码

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

我正在使用Eclipse 4.6.0和Java 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;
}

1 回答

  • 13

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

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

    即使您使用了变量参数功能,例如使用 someMethod(Foo.A); ,隐式数组创建将创建一个具有可重新类型的数组,即在调用站点处已知变量参数类型为 Foo ,这是在编译时已知的具体类型,因此数组创建很好 .

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

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

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

    这种潜在的错误类型的数组并不总是一个问题 . 大多数情况下,varargs方法将简单地迭代数组并从中获取元素 . 在这种情况下,数组的运行时类是无关紧要的;重要的是元素是 E (实际上它们是) . 如果是这种情况,您可以声明方法 @SafeVarargs . 但是,如果您的方法实际使用传递的数组的运行时类(例如,如果您将varargs数组作为类型 E[] 返回,或者您使用类似 Arrays.copyOf() 的东西来创建具有相同运行时类的数组),那么错误的运行时类数组将导致问题,你不能使用 @SafeVarargs .

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

    private static void someMethod(Object... objects)
    

相关问题