第二十三条 不要在新代码中使用原生态类型

泛型,出现在java 1.5 版本。之前,往一个集合里面添加数据,取出时,对象都要进行强转操作,如果不小心添加进去其他的错误类型的对象,那么在强转时会出错,程序运行起来了发现错误了。泛型的作用是,明确告诉编译器这个集合中需要哪种类型的对象,编译器自动对对象进行转化,一旦插入错误的对象,编译器直接报错,根本不用等到运行时才出错,提前知道了错误,就可以马上修改。泛型,一般用于类和方法,声明参数类型的,比如 ArrayList<E>,读作 E的数组列表。这种用法,直接在类或者接口后面,添加尖括号,里面添加对象类型,还是 ArrayList 举例,ArrayList<String>   ArrayList<Integer>, 就是这样。如果ArrayList 不适用泛型,我们想往里面添加单一的 String 字符串,则

  ArrayList numList = new ArrayList<>();
        numList.add("1");
        numList.add("2");
        numList.add(10);
一不小心添加了个 int 类型的数字,此时,编译是不会报错的,如果我们接下来要遍历每个对象,则

  Iterator iterator = numList.iterator();
        while (iterator.hasNext()){
            Object next = iterator.next();
            String s = (String) next;
        }

强转的这一步,numList 的第三个元素是Integer类型,却把它转换为String类型,则会报错,类型转换异常。这种是程序运行了才发现问题,代价就比较高了,如果加入泛型,则

 ArrayList<String> numList = new ArrayList<>();
        numList.add("1");
        numList.add("2");
        numList.add(10); // 报红 直接提示出错,

        Iterator<String> iterator = numList.iterator();
        while (iterator.hasNext()){
            String next = iterator.next();
        }

加入泛型,直接报错,这是后发现,哦,手误写错了,马上改过来。编译期就发现问题,提早解决,减轻成本。同时在迭代器里面的方法,不用自己转换对象,泛型检查后,自动帮我们转换好了。int和String这两种比较明显,如果细心点,是不会出错的,但如果是 Date 对象,一个是 java.sql.Date ,另一个是 java.util.Date,不同的包下面相同的类名,这种就比较容易弄混了,如果把不同类型的对象加入了没有使用泛型的集合中,那后果,可想而知。

集合如果不配合泛型,也能使用;配合了,效果更好。那为什么源码还是会兼容不使用泛型的写法呢,因为泛型出现时,java已经十岁了,之前的原生态集合已经被使用的相当广泛了,只能做兼容。但,新出的代码,需要加上泛型,提高效率和安全性。

以ArrayList为例,如果我们去读他的源码,会发现它的底层数据结构是一个 Object 类型的数组, Object[] array; 它支持所有的类型,泛型只是为了让集合元素类型的单一化。但如果我们在泛型里面添加 Object 呢?那么问题来了,ArrayList<String>  ArrayList<Object> 和 ArrayList 比较,有何不同呢。ArrayList<String> 很明显,意味着该集合只接受 String 类型的字符串,不接受其他类型;ArrayList<Object> 和 ArrayList 有点费解,实际上他俩的可接受的对象类型形同,都是任意类型都接受,不同的是,ArrayList<Object> 是检查对象类型,但明确告诉你,任意类型都行;ArrayList 是不检查,直接可以往里面添加类型。 举个不恰当的例子,大家坐地铁、火车时要安检,ArrayList<String> 相当于 检查了你随身携带物,只有不危险的才能带进去,ArrayList<Object>则相当于对你的携带物进行了检查,但也仅仅是做了检查,然后不管你带的是食品还是汽油或者一米长的水果刀,总之检查完了都放你进去,ArrayList 则 更牛了,检查都不检查,直接放你进去。 如果使用原生态的ArrayList,会失掉安全性,但使用ArrayList<Object>,则不会。


    private void unSafeAdd(List list, Object o){
        list.add(o);
    }

    ArrayList<String> list = new ArrayList<>();
    unSafeAdd(list, 2);

这个方法编译时,不会出错,但运行时,就报错了,list已经限定了只接受String类型,但调用unSafeAdd()方法,则直接添加了一个int,所以出错了。如果修改该方法,加入泛型

    private void unSafeAdd(List<Object> list, Object o){
        list.add(o);
    }

    ArrayList<String> list = new ArrayList<>();
    unSafeAdd(list, 2);

编译时,unSafeAdd()方法直接报错,这样就安全多了。还可以改成通配符的形式,与上面的效果类似,

    private <T> void unSafeAdd(List<T> list, T o){
        list.add(o);
    }

书中另外一个例子,

    static int numElementInCommon(Set s1, Set s2){
        int result = 0;
        for(Object object : s1){
            if(s2.contains(object)){
                result++;
            }
        }
        return result;
    }

这种可以实现功能,但比较危险,逃避了检查。对于这种任何了类型都可以的方法,我们加上泛型,可以

    static int numElementInCommon(Set<Object> s1, Set<Object> s2){
        int result = 0;
        for(Object object : s1){
            if(s2.contains(object)){
                result++;
            }
        }
        return result;
    }

介绍另外一种写法,无限通配符

    static int numElementInCommon(Set<?> s1, Set<?> s2){
        int result = 0;
        for(Object object : s1){
            if(s2.contains(object)){
                result++;
            }
        }
        return result;
    }

所以泛型好处比较多,写集合等时要用上,不要使用原生态的写法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值