我理解当Java 7使用通用类型的VARARGS时会发生这种情况;
但我的问题是……
当Eclipse说"它的使用可能会污染堆"时,它究竟意味着什么?
和
新的@SafeVarargs注释是如何阻止这种情况的?
- 细节在这里:docs.oracle.com JavaSE /规格/ / / /的HTML / se7 JLS & hellip;
- 重复的条目可能被作为Java 1.7 unchecked预警功能
- 我看到这个在我Possible heap pollution from parameterized vararg type编辑器:
堆污染是一个技术术语。它指的是引用的类型不是它们指向的对象的父类型。
1 2
| List <A > listOfAs = new ArrayList <>();
List listOfBs = (List)(Object)listOfAs ; // points to a list of As |
这可能导致"无法解释"的ClassCastExceptions。
1 2
| // if the heap never gets polluted, this should never throw a CCE
B b = listOfBs.get(0); |
@SafeVarargs并不能阻止这一点。然而,有一些方法可以证明不会污染堆,编译器只是无法证明它。以前,这样的API的调用者会收到恼人的警告,这些警告完全没有意义,但必须在每个调用站点都被禁止。现在,API作者可以在声明站点抑制它一次。
但是,如果该方法实际上不安全,则不会再警告用户。
- 那么我们是说堆被污染了,因为它包含的引用的类型不是我们所期望的类型吗?(示例中的listvs list)
- 我发现了一个关于程序员堆污染的解释。
- 这个答案很好地解释了什么是堆污染,但它并不能真正解释为什么varargs极有可能导致堆污染,以至于需要一个特定的警告。
- 我也是,我缺少如何确保我的代码不包含此问题的信息(例如,我如何知道它已经足够硬,可以添加@safevarargs)
当你申报
编译程序将其转换为
public static void foo(List[] bar)然后到
public static void foo(List[] bar)
这样就会产生这样的危险:您将错误地将不正确的值分配到列表中,编译器将不会触发任何错误。例如,如果T是String代码,那么以下代码将无错误地编译,但在运行时会失败:
1 2 3 4 5 6 7 8 9
| // 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 method could cause heap pollution from non-reifiable varargs parameter
而且你确信你的使用是安全的,那么你应该使用@SuppressWarnings("varargs")。请参见@safevarargs是否是此方法的适当注释?以及https://stackoverflow.com/a/14252221/14731,可以很好地解释第二种错误。
参考文献:
- http://docs.oracle.com/javase/7/docs/technotes/guides/language/non-reifiable-varargs.html
- http://DOCS.Oracle .COM/Javase/Tudio/Java/泛型/非ReIFiabl Valasepe。
- 我觉得我理解得更好。当你把varargs投给Object[]时,危险就来了。只要你不投注于Object[],听起来你应该很好。
- 举一个你能做的蠢事的例子:static void bar(T...args) { ((Object[])args)[0] ="a"; }。然后打电话给bar(Arrays.asList(1,2));。
- 这不会编译,因为bar[0]的类型是一个列表,而不是
- @ EranH。修复(我想)。现在试试看。
- @djeikyb如果危险只在我强制执行EDOCX1[1]时出现,为什么编译器会在我不执行时触发警告?毕竟,在编译时应该很容易检查这个(如果我没有将它传递给具有类似签名的另一个函数,在这种情况下,另一个函数应该触发一个警告)。我不相信这真的是警告的核心("如果你不投的话你是安全的"),我仍然不明白在哪种情况下我是好的。
- @Djeikyb如果没有参数化的varargs(例如bar(Integer...args)),您可能会做同样的蠢事。那么这个警告的意义是什么呢?
- @Vasiliyvlasov此问题仅与参数化varargs相关。如果您尝试对非类型化数组执行相同的操作,运行时将阻止您向数组中插入错误的类型。编译器警告您,运行时将无法阻止不正确的行为,因为参数类型在运行时未知(相反,数组在运行时确实知道其非泛型元素的类型)。
@SafeVarargs并不能阻止这种情况的发生,但是它要求编译器在编译使用它的代码时更严格。
http://docs.oracle.com/javase/7/docs/api/java/lang/safevarargs.html进一步详细解释了这一点。
堆污染是指当您在通用接口上执行操作时得到一个ClassCastException,并且它包含另一个未声明的类型。
当使用varargs时,它会导致创建一个Object[]来保存参数。
由于转义分析,JIT可以优化这个数组的创建。(这是我发现的少数次)它不能保证被优化掉,但是我不会担心它,除非你在你的内存分析器中看到它的问题。
afaik @SafeVarargs禁止编译器发出警告,不会更改JIT的行为方式。
- 有趣的是,虽然这并不能真正回答他关于@SafeVarargs的问题。
- 不。这不是堆污染的原因。"堆污染是指当一个参数化类型的变量引用的不是一个参数化类型的对象时,"REF:DOCS.Oracle。COM/JavaSe/Tuff/Java/Guogys/Helip;
原因是varargs提供了使用非参数化对象数组调用的选项。所以如果你的类型是list。,也可以使用list[]non varargs类型调用它。
下面是一个例子:
1 2 3 4 5 6 7 8
| public static void testCode (){
List[] b = new List[1];
test (b );
}
@SafeVarargs
public static void test (List <A >... a){
} |
正如您所看到的,列表[]b可以包含任何类型的使用者,但这段代码是编译的。如果使用varargs,则可以,但如果在类型擦除之后使用方法定义-void test(list[])-则编译器不会检查模板参数类型。@safevarargs将禁止此警告。