eclipse 通过varargs参数可能造成堆污染

qyswt5oh  于 2022-12-23  发布在  Eclipse
关注(0)|答案(6)|浏览(125)

我知道在Java7中使用varargs和泛型类型时会发生这种情况;
但我的问题是。
Eclipse说“它的使用可能会潜在地污染堆”,这到底是什么意思?
还有
新的@SafeVarargs注解如何防止这种情况?

093gszye

093gszye1#

堆污染是一个技术术语。它指的是引用的类型不是它们所指向对象的超类型。

List<A> listOfAs = new ArrayList<>();
List<B> listOfBs = (List<B>)(Object)listOfAs; // points to a list of As

这可能导致“无法解释”的ClassCastException

// if the heap never gets polluted, this should never throw a CCE
B b = listOfBs.get(0);

@SafeVarargs完全不能防止这种情况。然而,有些方法可能不会污染堆,编译器只是无法证明这一点。以前,此类API的调用者会收到烦人的警告,这些警告完全没有意义,但必须在每个调用点都取消。现在API作者可以在声明点取消一次警告。
但是,如果该方法实际上是 * 不 * 安全的,用户将不再受到警告。

ohtdti5x

ohtdti5x2#

当你宣布
public static <T> void foo(List<T>... bar)编译器将其转换为
public static <T> void foo(List<T>[] bar)然后到
public static void foo(List[] bar)
这样一来,你就有可能错误地将不正确的值赋值到列表中,而编译器不会触发任何错误。例如,如果TString,那么下面的代码将编译无误,但在运行时会失败:

// First, strip away the array type (arrays allow this kind of upcasting)
Object[] objectArray = bar;

// Next, insert an element with an incorrect type into the array
objectArray[0] = Arrays.asList(new Integer(42));

// Finally, try accessing the original array. A runtime error will occur
// (ClassCastException due to a casting from Integer to String)
T firstElement = bar[0].get(0);

如果您检查了方法以确保它不包含这样的漏洞,那么您可以使用@SafeVarargs注解它以抑制警告。对于接口,使用@SuppressWarnings("unchecked")
如果您收到此错误消息:
Varargs方法可能会由于不可定义的varargs参数而导致堆污染
并且您确信您的使用是安全的,那么您应该使用@SuppressWarnings("varargs")代替。请参见Is @SafeVarargs an appropriate annotation for this method?https://stackoverflow.com/a/14252221/14731以获得第二种错误的详细解释。
参考文献:

iibxawm4

iibxawm43#

@SafeVarargs不会阻止这种情况发生,但是它要求编译器在编译使用它的代码时更加严格。
http://docs.oracle.com/javase/7/docs/api/java/lang/SafeVarargs.html将对此进行更详细的解释。
堆污染是指在泛型接口上执行操作时得到一个ClassCastException,并且它包含另一个未声明的类型。

yzckvree

yzckvree4#

使用varargs时,可能会导致创建一个Object[]来保存参数。
由于逃逸分析,JIT可以优化掉这个数组创建。(我发现它这样做的次数不多)它不能保证被优化掉,但我不会担心它,除非你在内存分析器中看到它是一个问题。
AFAIK @SafeVarargs禁止编译器发出警告,并且不更改JIT的行为方式。

j8yoct9x

j8yoct9x5#

The reason is because varargs give the option of being called with a non-parametrized object array. So if your type was List < A > ... , it can also be called with List[] non-varargs type.
下面是一个例子:

public static void testCode(){
    List[] b = new List[1];
    test(b);
}

@SafeVarargs
public static void test(List<A>... a){
}

正如你所看到的,List [] b可以包含任何类型的消费者,但是这段代码可以编译。如果你使用varargs,那么你就可以了,但是如果你在type-erasure-void测试(List [])之后使用方法定义-那么编译器将不会检查模板参数类型。@SafeVarargs将抑制这个警告。

s5a0g9ez

s5a0g9ez6#

当你可以控制方法的调用方式时(例如类的私有方法),给方法添加@SafeVarargs注解是相当安全的。你必须确保只有声明的泛型类型的示例被传递给方法。
如果方法作为库对外公开,就很难捕捉到这样的错误,在这种情况下,最好避免这种注解,用集合类型(例如Collection<Type1<Type2>>)输入而不是varargs(Type1<Type2>...)重写解决方案。
至于命名,在我看来,术语 * 堆污染 * 现象是相当误导的。在documentation中,实际的JVM * 堆 * 并没有被提及。在软件工程中有一个question包含了一些关于这种现象命名的有趣想法。

相关问题