java 警告:Varargs 方法可能会因不可重用的 varargs 参数而造成堆污染

我在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 )
    {
    }
}

IntelliJ IDEA不会突出显示上述代码中的任何内容作为警告。但是,在编译时,以下行出现在“消息”视图的“制作”选项卡中:

警告:(L, C) java: Varargs 方法可能从不可再生的 varargs 参数 varargs 造成堆污染

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

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

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

注意:关于 varargs 方法的 stackoverflow 有很多问题,但似乎没有一个可以解决这个特定问题。事实上,整个interwebz在这个特定问题的答案上似乎相当糟糕。


答案 1

在我看来,我在这个问题上看到的答案都不令人满意,所以我想我会尝试一下。

以下是我的看法:

  1. @SafeVarargs
  • 禁止显示警告:。[unchecked] Possible heap pollution from parameterized vararg type Foo
  • 是方法协定的一部分,因此注释具有运行时保留期的原因。
  • 是对方法调用者的承诺,即该方法不会使用通用 varargs 参数弄乱堆。
  1. @SuppressWarnings("varargs")
  • 禁止显示警告:。[varargs] Varargs method could cause heap pollution from non-reifiable varargs parameter bar
  • 是解决方法代码发生的问题的方法,而不是方法的协定,因此为什么注释只有源代码保留
  • 告诉编译器,它不需要担心方法代码调用的被调用方方法使用不可重用 varargs 参数生成的数组弄乱堆。

因此,如果我对OP的原始代码进行以下简单的变体:

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

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

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

我可以通过标记为.这既使警告消失,通过向方法协定添加 varargs 安全性,确保任何调用的人都不必抑制任何 varargs 警告。bar()@SafeVarargsbar

但是,这也使Java编译器更仔细地查看方法代码本身 - 我猜是为了验证可能违反我刚刚使用的合同的简单情况。它看到调用传入和数字,因为需要一个由于类型擦除,可能会弄乱堆,从而导致传递它。bar()@SafeVarargsbar()baz()barArgsbaz()Object[]baz()bar()

因此,我还需要添加 to 以使有关 代码的警告消失。@SuppressWarnings("varargs")bar()bar()


答案 2

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

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);
    }
}

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

但是,如果你替换数组[1] = new Integer(1);使用三个注释行(即将 a 放入数组中),由于类型擦除,不会引发异常,也不会发生编译错误。ArrayList<Integer>

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