java警告:Varargs方法可能会导致来自不可恢复的varargs参数的堆污染

Mik*_*kis 9 java generics intellij-idea variadic-functions

我在JDK 1.8上使用IntelliJ IDEA和javac.我有以下代码:

class Test<T extends Throwable>
{
    @SafeVarargs
    final void varargsMethod( Collection<T>... varargs )
    {
        arrayMethod( varargs );
    }

    void arrayMethod( Collection<T>[] args )
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

IntelliJ IDEA不会将上述代码中的任何内容突出显示为警告.但是,在编译时,"消息"视图的"生成"选项卡中会显示以下行:

警告:(L,C)java:Varargs方法可能会导致不可恢复的varargs参数varargs造成堆污染

注意#1:我已经指定了@SafeVarargs.

注意#2:Warning:(L,C)指向varargs作为参数传递给arrayMethod()

假设我知道我在做什么,并且假设我非常确定不存在堆污染,或者我保证不会以某种可能导致堆污染的时髦方式调用此方法,我需要什么要压制此警告消息?

注意:有关varargs方法的stackoverflow存在大量问题,但似乎没有解决此特定问题的问题.事实上,整个interwebz对这个特定问题的答案似乎相当差.

0xb*_*7ed 10

我在这个问题上看到的答案似乎都不令人满意所以我认为我会抓住它.

这是我看到它的方式:

  1. @SafeVarargs

    • 是警告的治疗方法:[unchecked] Possible heap pollution from parameterized vararg type Foo.
    • 是方法契约的一部分,因此注释具有运行时保留的原因.
    • 是方法调用者的承诺,该方法不会使用通用的varargs参数搞乱堆.
  2. @SuppressWarnings("varargs")

    • 是警告的治疗方法:[varargs] Varargs method could cause heap pollution from non-reifiable varargs parameter bar.
    • 对发生的问题治愈的方法的代码,而不是方法的合同,因此为什么注解只有源代码保留.
    • 告诉编译器它不需要担心方法代码调用的callee方法使用不可重新生成的varargs参数产生的数组搞乱堆.

因此,如果我对OP的原始代码采取以下简单的变化:

class Foo {
    static <T> void bar(final T... barArgs) {
        baz(barArgs);
    }
    static <T> void baz(final T[] bazArgs) { }
}
Run Code Online (Sandbox Code Playgroud)

$ javac -Xlint:all Foo.java使用Java 9.0.1编译器的输出是:

Foo.java:2: warning: [unchecked] Possible heap pollution from parameterized vararg type T
    static <T> void bar(final T... barArgs) {
                                   ^
  where T is a type-variable:
    T extends Object declared in method <T>bar(T...)
1 warning
Run Code Online (Sandbox Code Playgroud)

我可以通过标记bar()来消除警告@SafeVarargs.这两者都会使警告消失,并且通过在方法合同中添加varargs安全性,确保调用的任何人bar都不必禁止任何varargs警告.

但是,它也使Java编译器更仔细地查看方法代码本身 - 我想是为了验证bar()可能违反我刚刚签订的合同的简单情况@SafeVarargs.而且它看到bar()所调用baz()传递barArgs和数据,因为baz()需要一个Object[]因类型擦除,baz()会搞乱了堆,从而导致bar()以传递地做到这一点.

所以我还需要添加@SuppressWarnings("varargs")以便bar()bar()代码的警告消失.


ton*_*929 5

事实上,您不应该以这种方式编写代码。考虑以下示例:

import java.util.*;

class Test<T extends Throwable>
{
    @SafeVarargs
    @SuppressWarnings("varargs")
    final void varargsMethod( Collection<T>... varargs )
    {
        arrayMethod( varargs );
    }

    void arrayMethod( Collection<T>[] args )
    {
        Object[] array = args;
        array[1] = new Integer(1);
        //
        //ArrayList<Integer> list = new ArrayList<>();
        //list.add(new Integer(1));
        //array[1] = list;
    }

    public static void main(String[] args)
    {
        ArrayList<Exception> list1 = new ArrayList<>();
        ArrayList<Exception> list2 = new ArrayList<>();
        (new Test<Exception>()).varargsMethod(list1, list2);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您运行代码,您将看到一个 ArrayStoreException,因为您将一个 Integer 放入Collection<T>数组中。

但是,如果替换 array[1] = new Integer(1); 用三个注释行(即把anArrayList<Integer>放入数组),由于类型擦除,不会抛出异常,也不会发生编译错误。

您想要一个Collection<Exception>数组,但现在它包含一个ArrayList<Integer>. 这是非常危险的,因为您不会意识到存在问题。

  • 这取决于“@SafeVarargs”的含义。我的感觉是,“@SafeVarargs”是一个承诺,即该方法不会对 varargs 参数执行任何不安全的操作,并且应该*包括*您传递此参数来处理它的方法。根据这种解释,如果“arrayMethod”像您的示例中那样使用它做了一些不安全的事情,那么您不应该对“varargsMethod”说“@SafeVarargs”。 (2认同)