关于List,List<Object>,List<?>三者的区别。

泛型的本质是类型参数化,是为了解决不确定具体对象类型时用到的办法,它的出现是一项伟大的发明,不仅解决了类型的校验问题,还可以提高代码的复用性。然而集合与泛型的联合使用,可以把泛型的功能发挥到极致。

 一:介绍 

        在单纯的List当中是没有类型的限制和赋值限定的,可以随意使用,但是如果天马行空的乱用,迟早会得到类型转换失败的异常。 很多程序员觉得List<Object>的用法完全等同于List,但是在接受其他泛型赋值时就会编译出错,List<?>是一个泛型,在没有赋值之前,表示它可以接受任何类型的集合赋值,但是赋值之后就不能随便在往里添加元素了。下面的例子很好的说明了三者的区别,以List为原型展开说明:

public class List {

	public static void main(String[] args) {
		
		//第一段 泛型出现之前的集合定义方式
		List a1 = new ArrayList();
		a1.add(new Object());
		a1.add(new Integer(11));
		a1.add(new String("String"));
		
		//第三段:把a1的引用赋值给a2,区别在于添加了泛型<Object>
		List<Object> a2 = a1;
		a2.add(new Object());
		a2.add(new Integer(12));
		a2.add(new String("String2"));
		
		//第三段:把a1的引用赋值给a3,区别在于添加了泛型<Integer>
		List<Integer> a3 = a1;
		a3.add(new Integer(13));
		//下方两行编译错误,不允许添加非Integer的类型值进入
		a3.add(new Object());
		a3.add(new String("String2"));
		
		//第四段 : 把a1的引用赋值给a4,区别在与增加了通配符
		List<?> a4 = a1;
		//允许删除和清除元素
		a1.remove(0);
		a4.clear();
		//不允许添加任何元素
		a4.add(new Object());
		
	}
}

第一段说明:在定义List之后,毫不犹豫的往里面添加三种不同类型的对象Integer,Object,String,遍历时没有问题,但是如果有一天别人在处理List中的数据时,误认为List中全是Integer类型或者String类型的数据,那么就会抛出ClassCastException异常。

第二段说明:把a1赋值给a2,a2是List<Object>类型,也可以再往里装入三种不同的对象,很多程序员认为List和List<Object>是完全相同的,至少从目前这两段看来是这样的。

第三段说明:由于泛型是在JDK5之后才出现的,考虑到向前兼容,因此历史代码有时候需要赋值给新泛型代码,从编译器角度是允许的,但是在实际的案例中有这么一个问题:

List<Integer> list = new ArrayList<Integer>(10);
		list.addAll(JsonObject.getJSONArray("level"));
		
		int amout = 0;
		for(Integer t: list) {
			//抛出ClassCastException异常,String can not be cast to Integer.
			if(condition) {
				amount = amount + t;
			}
		}

思考:为什么在addAll()的时候没有抛出异常,反而在遍历的时候抛出了呢?

首先我们看一下传入addAll()中的参数是一个JSONArray对象,JSONArray的定义如下:

public final class JSONArray extends AbstractJSON implements JSON, List {}

 实现了List接口,是非泛型集合,可以赋值给任何泛型限制的集合。编译的时候可以通过,但是在运行的时候就会报错,因为如果List集合有了泛型约束那么在这组数据中就只能存在一种类型数据,这是一个隐藏Bug,最终导致线上故障。所以说在JDK5之后我们在传入值的时候,如果存在泛型,则要使用对象的泛型定义的集合,类或者对象作为参数传入。

补充:

        如果说将a1的定义从List a1修改为List<Object> a1, 那么第三段就会编译出错。List<Object>赋值给List<Integer>是不允许的,若是反过来赋值。

List<Integer>  initList = new ArrayList<Integer>(3);
		initList.add(11);
		List<Object> objectList = intiList;

 事实上,编译也会报错,提示如下:

cannot be convered to java.util,List<java.lang.Object> 

而数组是可以这样赋值的,因为它是协变的,而集合不是!

协变性:数组的协变性是一种类型系统的特性,允许将子类类型的数组赋值给父类型的数组,这种特性在某些情况下可以提高代码的灵活性和可读性,但是需要小心使用,以免造成上面的编译通过,运行时错误的异常。

第四段说明:问号在正则表达式中可以匹配任何字符,List<?>称为通配符集合。它可以接受任何类型的集合引用赋值。

集合引用赋值:只是将引用(内存地址)赋值给了另一个变量,而不是复制了集合的内容。因此对其中一个集合的修改会影响到另一个集合,因此它们实际上指向了同一个集合对象。 

像上文一样。

List<?> a4 = a1;

这种情况下如果修改a4中的值,a1集合中的值也会受到影响不能添加任何元素,但可以remove和clear,List<?>一般作为参数来接收外部的集合,或者放回一个不知道的具体元素类型的集合。List<T>最大的问题是只能放置一种类型,如果随意转换类型就会出现异常。

二:总结 

        List集合在我们平时使用较为广泛,与Java泛型机制联合使用时,以确保集合中只存储指定类型的元素,可以提高类型的安全性,通过合理的使用泛型,可以避免类型转换错误和运行时异常。但是使用时也需要注意上面一些问题,以免造成上线的时的巨大漏洞。 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

香蕉炒肉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值