java 警告:可变参数方法可能会因不可具体化的可变参数参数而导致堆污染
java warning: Varargs method could cause heap pollution from non-reifiable varargs parameter
我在 JDK 1.8 上使用带有 javac 的 IntelliJ IDEA。我有以下代码:
class Test<T extends Throwable>
{
@SafeVarargs
final void varargsMethod( Collection<T>... varargs )
{
arrayMethod( varargs );
}
void arrayMethod( Collection<T>[] args )
{
}
}
IntelliJ IDEA 不会高亮显示上述代码中的任何内容作为警告。但是,在编译时,"Messages" 视图的 "Make" 选项卡中出现以下行:
Warning:(L, C) java: Varargs method could cause heap pollution from
non-reifiable varargs parameter varargs
注意#1:我已经指定了@SafeVarargs
。
注释 #2:Warning:(L,C)
指向 varargs
作为参数传递给 arrayMethod()
假设我知道我在做什么,并且假设我非常确定不会有堆污染,或者我保证我不会以某种可能导致堆污染的时髦方式调用此方法, 我需要做什么才能禁止显示此警告消息?
注意: Whosebug 上有很多关于可变参数方法的问题,但似乎有 none解决这个具体问题。事实上,整个 interwebz 似乎对这个特定问题的回答都很差。
需要额外的(而且看起来很多余)@SuppressWarnings( "varargs" )
来抑制警告,如下所示:
@SafeVarargs
@SuppressWarnings( "varargs" )
final void varargsMethod( Collection<T>... varargs )
{
arrayMethod( varargs );
}
事实上你不应该这样写你的代码。考虑以下示例:
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,因为您将整数放入 Collection<T>
数组。
但是,如果您替换 array[1] = new Integer(1);使用三个注释行(即将 ArrayList<Integer>
放入数组),由于类型擦除,不会抛出异常,也不会发生编译错误。
您想要一个 Collection<Exception>
数组,但现在它包含一个 ArrayList<Integer>
。这是非常危险的,因为您不会意识到存在问题。
这或许可以解释原因。我刚刚从 Effective Java 第 2 版第 25 条中复制了它。希望它能有所帮助。
The prohibition on generic array creation can be annoying. It means, for example, that it’s not generally possible for a generic type to return an array of its element type (but see Item 29 for a partial solution). It also means that you can get confusing warnings when using varargs methods (Item 42) in combination with generic types. This is because every time you invoke a varargs method, an array is created to hold the varargs parameters. If the element type of this array is not reifiable, you get a warning. There is little you can do about these warnings other than to suppress them (Item 24), and to avoid mixing generics and varargs in your APIs.
None 我在这个问题上看到的答案对我来说似乎是令人满意的,所以我想我会尝试一下。
这是我的看法:
@SafeVarargs
- 抑制警告:
[unchecked] Possible heap pollution from parameterized vararg type Foo
。
- 是方法的契约的一部分,因此为什么annotation has runtime retention.
- 是对方法调用者的承诺,该方法不会使用通用可变参数参数弄乱堆。
@SuppressWarnings("varargs")
- 抑制警告:
[varargs] Varargs method could cause heap pollution from non-reifiable varargs parameter bar
。
- 是解决发生在方法代码中的问题,而不是方法的契约,因此为什么annotation only has source code retention。
- 告诉编译器它不需要担心方法代码调用的 callee 方法 使用非具体化可变参数参数产生的数组弄乱堆.
因此,如果我对 OP 的原始代码进行以下简单修改:
class Foo {
static <T> void bar(final T... barArgs) {
baz(barArgs);
}
static <T> void baz(final T[] bazArgs) { }
}
$ 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
我可以通过将 bar()
标记为 @SafeVarargs
来消除该警告。这两者都使警告消失 和 ,通过向方法契约添加可变参数安全性,确保任何调用 bar
的人都不必抑制任何可变参数警告。
然而,它也使 Java 编译器更仔细地查看方法代码本身 - 我想是为了验证 bar()
可能违反我刚刚与@SafeVarargs
。它看到 bar()
调用 baz()
传入 barArgs
和数字,因为 baz()
由于类型擦除而采用 Object[]
,baz()
可能会搞砸堆,从而导致 bar()
传递它。
所以我还需要将 @SuppressWarnings("varargs")
添加到 bar()
以使关于 bar()
代码的警告消失。
Assuming that I know what I am doing
我拒绝假设这一点,因为这似乎 难以置信地 错误。
您 运行 遇到的情况是,因为泛型不可具体化,所以您要声明一个泛型数组,这通常是不受欢迎的。鉴于数组是 协变的 (String[]
是 Object[]
与 String
的方式相同,因此泛型和数组不能很好地混合是一个Object
),而泛型是不变的(List<String>
是不是一个List<Object>
,即使String
是 Object
).
如果你想要一个集合的集合...只需传递一个。它比混合数组和泛型更安全。
final void varargsMethod(Collection<<Collection<? super T>> collections) { }
我在 JDK 1.8 上使用带有 javac 的 IntelliJ IDEA。我有以下代码:
class Test<T extends Throwable>
{
@SafeVarargs
final void varargsMethod( Collection<T>... varargs )
{
arrayMethod( varargs );
}
void arrayMethod( Collection<T>[] args )
{
}
}
IntelliJ IDEA 不会高亮显示上述代码中的任何内容作为警告。但是,在编译时,"Messages" 视图的 "Make" 选项卡中出现以下行:
Warning:(L, C) java: Varargs method could cause heap pollution from non-reifiable varargs parameter varargs
注意#1:我已经指定了@SafeVarargs
。
注释 #2:Warning:(L,C)
指向 varargs
作为参数传递给 arrayMethod()
假设我知道我在做什么,并且假设我非常确定不会有堆污染,或者我保证我不会以某种可能导致堆污染的时髦方式调用此方法, 我需要做什么才能禁止显示此警告消息?
注意: Whosebug 上有很多关于可变参数方法的问题,但似乎有 none解决这个具体问题。事实上,整个 interwebz 似乎对这个特定问题的回答都很差。
需要额外的(而且看起来很多余)@SuppressWarnings( "varargs" )
来抑制警告,如下所示:
@SafeVarargs
@SuppressWarnings( "varargs" )
final void varargsMethod( Collection<T>... varargs )
{
arrayMethod( varargs );
}
事实上你不应该这样写你的代码。考虑以下示例:
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,因为您将整数放入 Collection<T>
数组。
但是,如果您替换 array[1] = new Integer(1);使用三个注释行(即将 ArrayList<Integer>
放入数组),由于类型擦除,不会抛出异常,也不会发生编译错误。
您想要一个 Collection<Exception>
数组,但现在它包含一个 ArrayList<Integer>
。这是非常危险的,因为您不会意识到存在问题。
这或许可以解释原因。我刚刚从 Effective Java 第 2 版第 25 条中复制了它。希望它能有所帮助。
The prohibition on generic array creation can be annoying. It means, for example, that it’s not generally possible for a generic type to return an array of its element type (but see Item 29 for a partial solution). It also means that you can get confusing warnings when using varargs methods (Item 42) in combination with generic types. This is because every time you invoke a varargs method, an array is created to hold the varargs parameters. If the element type of this array is not reifiable, you get a warning. There is little you can do about these warnings other than to suppress them (Item 24), and to avoid mixing generics and varargs in your APIs.
None 我在这个问题上看到的答案对我来说似乎是令人满意的,所以我想我会尝试一下。
这是我的看法:
@SafeVarargs
- 抑制警告:
[unchecked] Possible heap pollution from parameterized vararg type Foo
。 - 是方法的契约的一部分,因此为什么annotation has runtime retention.
- 是对方法调用者的承诺,该方法不会使用通用可变参数参数弄乱堆。
@SuppressWarnings("varargs")
- 抑制警告:
[varargs] Varargs method could cause heap pollution from non-reifiable varargs parameter bar
。 - 是解决发生在方法代码中的问题,而不是方法的契约,因此为什么annotation only has source code retention。
- 告诉编译器它不需要担心方法代码调用的 callee 方法 使用非具体化可变参数参数产生的数组弄乱堆.
因此,如果我对 OP 的原始代码进行以下简单修改:
class Foo {
static <T> void bar(final T... barArgs) {
baz(barArgs);
}
static <T> void baz(final T[] bazArgs) { }
}
$ 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
我可以通过将 bar()
标记为 @SafeVarargs
来消除该警告。这两者都使警告消失 和 ,通过向方法契约添加可变参数安全性,确保任何调用 bar
的人都不必抑制任何可变参数警告。
然而,它也使 Java 编译器更仔细地查看方法代码本身 - 我想是为了验证 bar()
可能违反我刚刚与@SafeVarargs
。它看到 bar()
调用 baz()
传入 barArgs
和数字,因为 baz()
由于类型擦除而采用 Object[]
,baz()
可能会搞砸堆,从而导致 bar()
传递它。
所以我还需要将 @SuppressWarnings("varargs")
添加到 bar()
以使关于 bar()
代码的警告消失。
Assuming that I know what I am doing
我拒绝假设这一点,因为这似乎 难以置信地 错误。
您 运行 遇到的情况是,因为泛型不可具体化,所以您要声明一个泛型数组,这通常是不受欢迎的。鉴于数组是 协变的 (String[]
是 Object[]
与 String
的方式相同,因此泛型和数组不能很好地混合是一个Object
),而泛型是不变的(List<String>
是不是一个List<Object>
,即使String
是 Object
).
如果你想要一个集合的集合...只需传递一个。它比混合数组和泛型更安全。
final void varargsMethod(Collection<<Collection<? super T>> collections) { }