本文介绍了“通过varargs参数的潜在堆污染".对于Enum< E> ...为什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此问题特定于对带有泛型 Enum< E> s的varargs的使用:

This question is specific to using varargs with generic-fied Enum<E>s:

为什么会收到此警告类型安全性:如果我定义如下方法,则会通过varargs参数元素造成潜在的堆污染:

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

与此相反:

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

因此,在声明方法 @SafeVarargs 之前,我应该注意什么?

Accordingly, what should I look out for before declaring the method @SafeVarargs?

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

This question is similar to these questions regarding Collection<T>... but the scenarios shown in those answers do not seem to apply with Enum<E>...:

这个问题的反面是为什么没有警告:

It is the reverse of this question which questions why there are no warning:

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

This is what I tried to pollute the heap, but each bad attempt results in java.lang.ArrayStoreException and not a polluted array.

我正在使用 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;
}

推荐答案

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

There is a warning for the varargs method because varargs methods can cause implicit array creation at the calling site, whereas the version that takes an array parameter doesn't. The way that varargs works is that it causes the compiler to create an array at the call site filled with the variable arguments, which is then passed to the method as a single array parameter. The parameter would have type E[], so the array created should be an E[].

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

First, in the call site in your example, you are not using the variable arguments functionality at all. You are passing the array of variable arguments directly. So there is no implicit array creation in this case.

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

Even if you had used the variable arguments functionality, e.g. with someMethod(Foo.A);, the implicit array creation would be creating an array with a reifiable type, i.e. the variable argument type is known at the call site to be Foo which is a concrete type known at compile-time, so array creation is fine.

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

The problem is only if the variable argument type at the call site is also a generic type or type parameter. For example, something like:

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

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

Then the compiler would need to create an array of this generic type or type parameter (it needs to create an E[]), but as you know generic array creation is not allowed in Java. It would instead create an array with the component type being the erasure of the generic type (in this example it would be Enum), so it would pass an array of the wrong type (in this example, the passed array should be an E[], but the array's actual runtime class would be Enum[] which is not a subtype of E[]).

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

This potential wrong type of array is not always a problem. Most of the time, a varargs method will simply iterate over the array and get elements out of it. In this case, the runtime class of the array is irrelevant; all that matters is that the elements are type E (which indeed they are). If this is the case, you can declare your method @SafeVarargs. However, if your method actually uses the runtime class of the passed array (for example, if you return the varargs array as type E[], or you use something like Arrays.copyOf() to create an array with the same runtime class), then a wrong runtime class of the array will lead to problems and you cannot use @SafeVarargs.

您的示例有点奇怪,因为,您甚至没有使用元素为 E 类型的事实,更不用说数组为 E [] .因此,您可以使用 @SafeVarargs ,不仅如此,您还可以声明数组首先采用 Object [] :

Your example is a bit weird because, well, you are not even using the fact that the elements are of type E, let alone the fact that the array is E[]. So you could use @SafeVarargs, and not only that, you could simply declare the array as taking Object[] in the first place:

private static void someMethod(Object... objects)

这篇关于“通过varargs参数的潜在堆污染".对于Enum&lt; E&gt; ...为什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-14 12:39