第二十九条 优先考虑类型安全的异构容器


泛型最常见的就是用于集合,例如 List Set Map 等,加入泛型一般就是加入了元素类型限制。 异构容器是指能容纳不同类型对象的容器,原生态的List Mpa 等时异构容器,但一旦使用了泛型参数,比如 List<String>  Map<String,String> ,他们就不是异构容器了,因为里面限制了只能加入 String 类型。我们知道,原生态类型的集合是不安全的,我们无法确定里面的元素到底是什么类型,比较容易出错。怎么才能建立一个有泛型的异构容器呢? 书中给出了 Favorites 的 要求,要能添加和提供数据。那么我们


public class Favorites {
    private Map<Class<?>, Object> favorites = new HashMap<>();

    public <T> void putFavorite(Class<T> type, T instance) {
        if (type == null)
            throw new NullPointerException("Type is null");
        favorites.put(type, instance);
    }

    public <T> T getFavorite(Class<T> type) {
        return type.cast(favorites.get(type));
    }
}

我们在 Favorites 里面添加一个 Map 集合,class 作为 key,这样,就能装进去各种不同类型的对象在这个 Favorites 对象里, 同时根据 class 获取对应的 value 值,我们再看一下 cast()方法的代码,在 Class 类中

    public T cast(Object obj) {
        if (obj != null && !isInstance(obj))
            throw new ClassCastException(cannotCastMsg(obj));
        return (T) obj;
    }

type.cast(favorites.get(type)); 我们根据 class 的类型,从map集合中获取到对应的value,然后再代用Class 的 cast()方法,转换为自己class对应的类型,这样,一个异构容器就好了,我们调用一下,看看效果

        Favorites favorites = new Favorites();
        favorites.putFavorite(String.class, "Death");
        favorites.putFavorite(Integer.class, 25);

        String name = favorites.getFavorite(String.class);
        Integer num = favorites.getFavorite(Integer.class);
        
        System.out.println("name : " + name +"  num : " + num);
数据能存储,也能正常取出来。但是里面有些问题, 第一就是 如果传入的 class 和 对象 不一致,在 getFavorite()方法容易类型异常,此时添加检测机制
    public <T> void putFavorite(Class<T> type, T instance) {
        if (type == null)
            throw new NullPointerException("Type is null");
        favorites.put(type, type.cast(instance));
    }

调用 putFavorite 时,我们传入了A.class,但value值却是一个 A 的子类型 B ,这种情况是可以的,但如果想取出来,key 只能是 A.class,绝对不能是 B.class,这点要注意。有一些不能被泛型化的类型,比如 List<String>.class, 这种语法是错误的,不能当做 key 值, 这种暂时没好的解决方法。

题外话,比如一个 ArrayList<String> 类型的集合,如何把 int 类型的数据装进去? 如果是原生态的 ArrayList,很容易就add()进去了,但此时有了泛型限制。 我们知道泛型是编译时起作用,运行时擦除了泛型的痕迹;反射是编译时不起作用,运行时才执行代码功能。说到这,反应快的已经明白了,泛型和反射彼此错开了对方,这样,就给我们机会了

public static void test() {
        ArrayList<String> list = new ArrayList<>();
        list.add("death");
        try {
            Method method = list.getClass().getMethod("add", Object.class);
            method.invoke(list, 34);
            method.invoke(list, "zhangsan");
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("list : " + list.toString());
    }
打印结果是   list : [death, 34, zhangsan] 。 34被添加进去,以包装类的格式 Integer 。 这种方式仅仅添加数据成功,如果用迭代器遍历或者增强for遍历,不是所有的对象都能是String类型,需要做类型判断,防止类型异常。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值