java list 泛型 动态_Java泛型 动态类型安全的容器 java.util.Collections.checkedList()

大家都知道java的数组是动态类型安全的,因为数组在创建时就已经记住了其数组元素的类型,所以不管数组的引用怎么协变,如果插入不符合正确类型的对象了,数组就会报错。

而java的泛型容器相比就显得很脆弱,因为插入元素时的检查仅仅存在于编译期,其实就是根据引用的类型来的,要是List这样就根本不检查了,要是List这样的就只会针对于这个引用上调用的方法进行检查。换句话说,普通的泛型容器根本没记住创建时你给定的类型参数。

除了java.util.Collections.checkedList()方法外,还有checkedCollection、checkedMap、checkedSet、checkedSortedSet、checkedSortedMap,也可以返回动态类型安全的容器。

checkedList()返回动态类型安全的容器

import java.util.*;

class Pet{}

class Dog extends Pet {}

class Cat extends Pet {}

public class CheckedList {

@SuppressWarnings("unchecked")

static void oldStyleMethod(List probablyDogs) {

probablyDogs.add(new Cat());

}

public static void main(String[] args) {

List dogs1 = new ArrayList();

oldStyleMethod(dogs1); // Quietly accepts a Cat这里会插入一个错误类型的元素进入容器,插入时不会报错

List dogs2 = Collections.checkedList(

new ArrayList(), Dog.class);

try {

oldStyleMethod(dogs2); // Throws an exception这里插入错误类型元素进入容器时,会抛出运行时异常

} catch(Exception e) {

System.out.println(e);

}

// Derived types work fine:子类对象放入父类的容器里,是可以的

List pets = Collections.checkedList(

new ArrayList(), Pet.class);

pets.add(new Dog());

pets.add(new Cat());

}

} /* Output:

java.lang.ClassCastException: Attempt to insert class typeinfo.pets.Cat element into collection with element type class typeinfo.pets.Dog

*///:~

oldStyleMethod方法将传入的泛型容器作为原生类型使用,这时编译器不会进行编译期的检查。

List dogs2 = Collections.checkedList(new ArrayList(), Dog.class);通过checkedList方法返回了一个动态类型安全的List,此时,oldStyleMethod(dogs2);处理时,依靠dogs2对象自身的方法,便可以在插入元素时判断元素类型正确与否。

oldStyleMethod(dogs1);这里会插入一个错误类型的元素进入容器,插入时不会报错;oldStyleMethod(dogs2);这里插入错误类型元素进入容器时,会抛出运行时异常。相比之下,dogs2更加安全,因为错误类型元素在插入就报错,而不是推迟到取出元素强转类型再报错。

checkedList()源码分析

下面是checkedList方法体:

//java.lang.Collections

public static List checkedList(List list, Class type) {

return (list instanceof RandomAccess ?

new CheckedRandomAccessList<>(list, type) :

new CheckedList<>(list, type));

}

发现方法逻辑这句list instanceof RandomAccess,要先去判断list形参是否为RandomAccess的实例,这个RandomAccess是个啥呢:

/**

* Marker interface used by List implementations to indicate that

* they support fast (generally constant time) random access. The primary

* purpose of this interface is to allow generic algorithms to alter their

* behavior to provide good performance when applied to either random or

* sequential access lists.

*/

public interface RandomAccess {

}

原来RandomAccess只是一个用作标记的接口,代表容器可以随机存取,类似于数组可以用下标取元素一样,有以下具体类实现了RandomAccess接口:

1d3472530734e43aebda064435441d76.png

果然,实现了RandomAccess接口的容器们基本都可以通过索引index来存或者取,除了Stack。回到checkedList的代码逻辑,由于本文例子是ArrayList,所以三目表达式会判断为真,然后调用CheckedRandomAccessList的构造器:

static class CheckedRandomAccessList extends CheckedList

implements RandomAccess

{

private static final long serialVersionUID = 1638200125423088369L;

CheckedRandomAccessList(List list, Class type) {

super(list, type);

}

public List subList(int fromIndex, int toIndex) {

return new CheckedRandomAccessList<>(

list.subList(fromIndex, toIndex), type);

}

}

原来CheckedRandomAccessList是Collections里的静态内部类呀(其实java集合源码经常这么搞静态内部类),看到它构造时把参数传给了父类CheckedList的构造器:

static class CheckedList

extends CheckedCollection

implements List

{

private static final long serialVersionUID = 65247728283967356L;

final List list;

CheckedList(List list, Class type) {

super(list, type);

this.list = list;

}

//省略

}

CheckedList又是一个静态内部类,它构造时又把参数传给了父类CheckedCollection的构造器:

static class CheckedCollection implements Collection, Serializable {

private static final long serialVersionUID = 1578914078182001775L;

final Collection c;

final Class type;

@SuppressWarnings("unchecked")

E typeCheck(Object o) {

if (o != null && !type.isInstance(o))

throw new ClassCastException(badElementMsg(o));

return (E) o;

}

private String badElementMsg(Object o) {

return "Attempt to insert " + o.getClass() +

" element into collection with element type " + type;

}

CheckedCollection(Collection c, Class type) {

this.c = Objects.requireNonNull(c, "c");

this.type = Objects.requireNonNull(type, "type");

}

CheckedCollection又是一个静态内部类,构造器里面检查了两个参数是否为null,至此,构造器终于构造好了。看到这里,你应该发现了,直到构造完成,它也没有检查传入的容器是否传入时已经有了错误类型的元素,所以它只能保证你checkedList()返回后的容器是插入安全。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值