转载自:http://softlab.sdut.edu.cn/blog/subaochen/2017/04/safevarargs%E7%9A%84%E7%94%A8%E6%B3%95/

@SafeVarargs在JDK 7中引入,主要目的是处理可变长参数中的泛型,此注解告诉编译器:在可变长参数中的泛型是类型安全的。可变长参数是使用数组存储的,

而数组和泛型不能很好的混合使用[effective-java, p105,第25条:列表优先于数组]

1、

简单的说,数组元素的数据类型在编译和运行时都是确定的,而泛型的数据类型只有在运行时才能确定下来,因此当把一个泛型存储到数组中时,编译器在编译阶段
无法检查数据类型是否匹配,因此会给出警告信息:存在可能的“堆污染”(heap pollution),即如果泛型的真实数据类型无法和参数数组的类型匹配,会导致ClassCastException异常。

,因此当在可变长参数中使用泛型时,编译器都会给出警告信息。考虑#[java-7-new-feature-cookbook, p35]

package cn.edu.sdut.softlab.safevarargs;

import java.util.ArrayList;

/**
* Created by subaochen on 17-4-3.
*/
public class SafeVarargsTest {
public static void main(String[] args) {
ArrayList<Integer> a1 = new ArrayList<>();
a1.add(new Integer(1));
a1.add(2); showArgs(a1, 12);
} //@SafeVarargs
public static <T> void showArgs(T... array) {
for (T arg : array) {
System.out.println(arg.getClass().getName() + ":" + arg);
}
} }

当我们使用-Xlint:unchecked参数编译此代码时

2、

如果使用IDE进行编译,需要修改编译参数,增加-Xlint:unchecked编译选项。

,有如下的警告信息:

$ javac -Xlint:unchecked cn/edu/sdut/softlab/safevarargs/SafeVarargsTest.java
cn/edu/sdut/softlab/safevarargs/SafeVarargsTest.java:18: 警告: [unchecked] 参数化 vararg 类型T的堆可能已受污染
public static <T> void showArgs(T… array) {
^
其中, T是类型变量:
T扩展已在方法 <T>showArgs(T…)中声明的Object
1 个警告
但是显然在这个示例中,可变参数的泛型是安全的,因此可以启用#中的@SafeVarargs注解消除这个警告信息。请读者自行验证。
@SafeVarargs注解只能用在参数长度可变的方法或构造方法上,且方法必须声明为static或final,否则会出现编译错误。一个方法使用@SafeVarargs注解的前提是,
开发人员必须确保这个方法的实现中对泛型类型参数的处理不会引发类型安全问题,否则可能导致运行时的类型转换异常。下面给出一个“堆污染”的实例,参见1
 
package cn.edu.sdut.softlab.safevarargs;

import java.util.Arrays;
import java.util.List; /**
* Created by subaochen on 17-4-3.
* 本例取自SafeVarargs的javadoc文档
*/
public class UnsafeMethodTest { public static void main(String[] args) {
List&lt;String&gt; list1 = Arrays.asList("one", "two");
List&lt;String&gt; list2 = Arrays.asList("three","four");
unsafeMethod(list1, list2);
} @SafeVarargs // 其实并不安全!
static void unsafeMethod(List&lt;String&gt;... stringLists) {
Object[] array = stringLists;
List&lt;Integer&gt; tmpList = Arrays.asList(42, 56);
array[0] = tmpList; // tmpList是一个List对象(类型已经擦除),赋值给Object类型的对象是允许的(向上塑型),能够编译通过
String s = stringLists[0].get(0); // 运行时抛出ClassCastException!
}
}
运行UnsafeMethodTest的结果如下:
Exception in thread “main” java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at cn.edu.sdut.softlab.safevarargs.UnsafeMethodTest.unsafeMethod( UnsafeMethodTest.java:22 )
at cn.edu.sdut.softlab.safevarargs.UnsafeMethodTest.main( UnsafeMethodTest.java:14 )
对这个结果意外吗?我们来详细分析一下。在1中,当代码执行到第22行时的状态如1所示,数组array和stringLists同时指向了参数数组,tmpList是一个包含两个Integer对象的list对象。
 
图 1: 可变长参数的初始状态
当程序执行到
 
3、
这里的赋值操作是合法的,因为tmplist是List类型的对象,array是一个Object类型的数组,根据Java允许“向上塑型”的原则,array数组能够接受任意类型的对象。
array[0] = tmpList;

时,几个变量的关系如2所示,虚线表示原先的指向关系,实线表示新的指向关系。此时,参数数组的第0个元素指向了包含两个Integer对象的list对象tmpList。当进一步执行:

   String s = stringLists[0].get(0);
时,从参数数组中取出第0个元素为list对象(tmpList),再取出list对象的第0个元素为Integer类型的对象(其值为42)。问题在这里出现了,我们试图将一个Integer类型的对象赋值给String类型的对象,显然会导致类型转换异常(ClassCastException)。
因此,这个方法是不应该标记为@SafeVarargs的。
图 2: 可变长参数遭到堆污染

SafeVarargs的用法的更多相关文章

  1. @SafeVarargs 使用说明

    说明: @SafeVarargs 是jdk1.7引入的适用于可变参数与泛型能够更好结合的一个注解. 官方解释: 程序员认定带有注释的主体或者构造函数不会对其执行潜在的不安全操作 将此注释应用于未经检查 ...

  2. EditText 基本用法

    title: EditText 基本用法 tags: EditText,编辑框,输入框 --- EditText介绍: EditText 在开发中也是经常用到的控件,也是一个比较必要的组件,可以说它是 ...

  3. jquery插件的用法之cookie 插件

    一.使用cookie 插件 插件官方网站下载地址:http://plugins.jquery.com/cookie/ cookie 插件的用法比较简单,直接粘贴下面代码示例: //生成一个cookie ...

  4. Java中的Socket的用法

                                   Java中的Socket的用法 Java中的Socket分为普通的Socket和NioSocket. 普通Socket的用法 Java中的 ...

  5. [转载]C#中MessageBox.Show用法以及VB.NET中MsgBox用法

    一.C#中MessageBox.Show用法 MessageBox.Show (String) 显示具有指定文本的消息框. 由 .NET Compact Framework 支持. MessageBo ...

  6. python enumerate 用法

    A new built-in function, enumerate() , will make certain loops a bit clearer. enumerate(thing) , whe ...

  7. [转载]Jquery中$.get(),$.post(),$.ajax(),$.getJSON()的用法总结

    本文对Jquery中$.get(),$.post(),$.ajax(),$.getJSON()的用法进行了详细的总结,需要的朋友可以参考下,希望对大家有所帮助. 详细解读Jquery各Ajax函数: ...

  8. 【JavaScript】innerHTML、innerText和outerHTML的用法区别

    用法: <div id="test">   <span style="color:red">test1</span> tes ...

  9. chattr用法

    [root@localhost tmp]# umask 0022 一.chattr用法 1.创建空文件attrtest,然后删除,提示无法删除,因为有隐藏文件 [root@localhost tmp] ...

随机推荐

  1. go语言日期时间格式化

    原文: http://www.golangprograms.com/get-current-date-and-time-in-various-format-in-golang.html package ...

  2. API接口防止参数篡改和重放攻击

    {近期领导要求我对公司业务的支付类的ocr接口做研究,是否存在支付接口重放攻击,so.....} API重放攻击(Replay Attacks)又称重播攻击.回放攻击.他的原理就是把之前窃听到的数据原 ...

  3. 【方法】如何实现图片压缩并使用FormData上传

    在前端上传图片的操作过程中,当上传服务器时,如果图片过大,可能会影响页面响应速度,这个时候,我们便会对图片进行压缩处理,再上传服务器. 前端对图片进行压缩,一般使用canvas来实现.最后使用canv ...

  4. Luogu P5048 [Ynoi2019模拟赛]Yuno loves sqrt technology III 分块

    这才是真正的$N\sqrt{N}$吧$qwq$ 记录每个数$vl$出现的位置$s[vl]$,和每个数$a[i]=vl$是第几个$vl$,记为$P[i]$,然后预处理出块$[i,j]$区间的答案$f[i ...

  5. 路由器配置——RIP路由

    一.实验目的:用rip路由实现全网互通 二.拓扑图: 三.具体步骤配置 (1)R1路由器配置 Router>enable  --进入特权模式Router#configure terminal   ...

  6. 「CF712E」Memory and Casinos「线段树」「概率」

    题解 解法1:(官方做法) 一段区间的\(L\)定义为从最左边开始出发,最左不失败,一直到最右边胜利的概率,\(R\)定义为从最右边开始出发,最左不失败,又回到最右边胜利的概率 考虑一个区间\([l, ...

  7. php &#编码/php unicode转码/php &#数字编码

    今天使PHP开发用到了Unicode的编码与解码,将unicode转为中文,再将中文转Unicode这样的操作是非常常见的,所以小编将这两个unicode中文互转函数给作为一个笔记保存起来,非常的简单 ...

  8. Vue_(Router路由)-vue-router路由的基本用法

    vue-router官网:传送门 vue-router起步:传送门 vue-router路由:Vue.js官网推出的路由管理器,方便的构建单页应用 单页应用:Single Page Applicati ...

  9. Java数组分配内存空间

    分配内存空间 数组名=new 数据类型[数组长度]: new关键字用来实现为数组或对象分配内存 数组具有固定的长度.获取数组的长度: 数组名.length 定义数组+分配内存空间 数据类型[]数组名= ...

  10. django 快速实现注册(四)

    一.创建项目与应用  #创建项目fnngj@fnngj-H24X:~/djpy$ django-admin.py startproject mysite3fnngj@fnngj-H24X:~/djpy ...