我在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不会将上述代码中的任何内容突出显示为警告。但是,在编译时,&#34; Make&#34; &#34;消息&#34;的标签;视图:
警告:(L,C)java:Varargs方法可能导致堆污染 不可恢复的varargs参数varargs
注意#1:我已经指定了@SafeVarargs
。
注意#2:Warning:(L,C)
指向varargs
作为参数传递给arrayMethod()
假设我知道我在做什么,并且假设我非常确定不存在堆污染,或者我保证不会以某种可能导致堆污染的时髦方式调用此方法, 我需要做些什么来抑制此警告消息?
注意: 有关varargs方法的stackoverflow存在大量问题,但似乎没有解决此特定问题的问题。事实上,整个interwebz对这个特定问题的回答似乎很差。
答案 0 :(得分:9)
我在这个问题上看到的答案在我看来都不令人满意,所以我想我会捅它。
以下是我的看法:
@SafeVarargs
[unchecked] Possible heap pollution from parameterized vararg type Foo
。 @SuppressWarnings("varargs")
[varargs] Varargs method could cause heap pollution from non-reifiable varargs parameter bar
。因此,如果我在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
我可以通过将bar()
标记为@SafeVarargs
来消除警告。这通过向方法合同添加varargs安全性使警告消失和,确保调用bar
的任何人都不必禁止任何varargs警告。
但是,它也使Java编译器更仔细地查看方法代码本身 - 我想是为了验证bar()
可能违反我刚用@SafeVarargs
签订的合同的简单情况。并且它看到bar()
调用baz()
传入barArgs
并且因为baz()
由于类型删除而导致Object[]
,因为baz()
可能会陷入困境堆,从而导致bar()
传递它。
因此,我还需要将@SuppressWarnings("varargs")
添加到bar()
,以便对bar()
代码的警告消失。
答案 1 :(得分:2)
需要额外的(并且非常多余的)@SuppressWarnings( "varargs" )
来抑制警告,如下所示:
@SafeVarargs
@SuppressWarnings( "varargs" )
final void varargsMethod( Collection<T>... varargs )
{
arrayMethod( varargs );
}
答案 2 :(得分: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>
数组中。
但是,如果你替换array [1] = new Integer(1);由于类型擦除,使用三个注释行(即将ArrayList<Integer>
放入数组中),不会抛出异常并且不会发生编译错误。
您希望拥有Collection<Exception>
数组,但现在它包含ArrayList<Integer>
。这是非常危险的,因为你不会意识到存在问题。
答案 3 :(得分:0)
假设我知道自己在做什么
我拒绝承担这一点,因为这似乎难以置信错误。
您遇到的情况是,由于泛型不可恢复,您要声明一个通用数组,这通常是不受欢迎的。鉴于数组协变(String[]
是Object[]
,与String
相同,泛型和数组不能很好地混合一个Object
),而泛型不变(List<String>
不一个List<Object>
,即使String
是Object
final void varargsMethod(Collection<<Collection<? super T>> collections) { }
)。
如果你想要一个集合的集合......只需传递一个。它比混合数组和泛型更安全。
propertyItem
答案 4 :(得分:-3)
这可能解释了原因。我刚从Effective Java第2版第25项中复制了它。希望它可以提供帮助。
禁止通用阵列创建可能很烦人。例如,这意味着泛型类型通常不可能返回其元素类型的数组(但是对于部分解决方案,请参见第29项)。 这也意味着当将varargs方法(第42项)与泛型类型结合使用时,您可能会收到令人困惑的警告。这是因为每次调用varargs方法时,都会创建一个数组来保存varargs参数。如果此数组的元素类型不可恢复,则会收到警告。除了禁止这些警告之外,您几乎无法做到这些警告(第24项),并避免在API中混合泛型和变量。