美文网首页
Java泛型——原生态类型与泛型

Java泛型——原生态类型与泛型

作者: NEU_PROYZ | 来源:发表于2018-07-12 15:34 被阅读34次

    最近在学习一些框架,有个必要的过程就是阅读源码,这是个比较需要耐心的过程,很多时候我都觉得扫一眼明白怎么个意思就行了,但越来越发现自己忽略了一件很重要的事情——代码的基本功底。很多时候在源代码里面会看到Class<?>类似的代码,知道它是在干什么,可细说又感觉闹不明白,之前有详细看过,但都忘了,深觉不输出永远学不会。于是,重新拿起书本,想再理一理泛型相关的知识。

    我想,泛型大家平时都在用,而且是业务代码必须的,写了很多却少有人关注泛型的内涵。问几个问题:
    1.集合和数组的区别。
    2.Java为什么引进泛型。
    3.List<Object>和List有区别吗。
    也许你能轻松地回答出前两个问题,那我们来看看你的答案是否正确。
    答1:集合和数组最大区别就是:集合对象类型不固定且不定大小,数组定类型且长度固定。嗯,起码基本的常识还是有的。问这个问题的原因是要引出第二个问题的答案。

    泛型:声明中具有一个或多个类型参数的类或接口就是泛型类或泛型接口,统称为泛型。如:List<String>
    所以你的答案是:泛型引入的目的是为了限制集合元素的类型。没错,我想大部分也停留在这个阶段了,因为我们平时写代码就只需要到这。那么,我接着问一个问题:
    不限制集合的类型参数会引起什么问题呢?(摘自《Effictive Java》,只是演示,不能运行的)

    //Contains only Stamp instances.
    private final Collection stamps = new Collection();
    stamps.add(new Coin());
    for(Iterator i = stamps.iterator();i.hasNext();){
        Stamp s = (Stamp)i.next();
    }
    

    其实显而易见,这段代码在运行时会出错,因为你不可能将Coin对象转换成Stamp对象。那么问题出在哪呢?问题就在集合里面的元素只通过注释说明,但编译器并不认识注释,但一旦交由人来做这件事情就很有可能出错。 所以,我们需要一种编译时的类型检查机制,让程序员在加入不符合泛型的类型对象时报错。所以,第二问题的答案需要加入编译期的类型自动检查

    每一种泛型都包含一种原生态类型,比如List<E>的原生态类型就是List,你很少能看见代码里面用这种原生态类型的接口,基本都是泛型,这是一种规范,但是不用泛型可不可以呢? 也是可以的。

    请不要在新代码中使用原生态类型:我们都知道泛型的优势,那么为什么不禁止大家使用原生态类型的代码呢?其实主要是为了保持向前的兼容性,泛型在Java1.5才出来,之前已经有大量的没有泛型的代码。这叫移植兼容性

    下面就是第三个问题了,讲道理能不能绕明白还得看自己。

    public static void main(String[] args){
        List<String> strings = new ArrayList<>();
        unsafeAdd(strings, new Integer(1));
        String s = strings.get(0);
    }
    private static void unsafeAdd(List list, Object object){
        list.add(object);
    }
    

    编译会不会有问题呢,不会。因为原生态类型是不会检查插入对象类型的。好,那么我们改变一下写法,将List改为List<Object>

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

    这样你再试试,你会发现main函数第二行报错了。下面,按照常理来思考一下,List = 集合 = 可以放任何类型的元素 = List<Object>。有问题吗?这就是我感觉一直没绕过的点。既然都Object了那还有泛型检查的必要?

    嗯,首先得明确两点。
    1.List和List<Object>,前者逃避了泛型检查,后者是肯定会泛型检查的。只是说,你在往List<Object>里面插入元素的时候,不管插入什么值好像都不会报错,因为Object是所有对象的父类嘛。
    2.泛型是不可变的(相对于数组的协变而言)。就是说,String是Object的子类,List<String>是List的子类, 但是List<String>不是List<Object>的子类,那么在泛型检查的时候就会报错。

    似乎是说得通的,但是更深层次的想一下,为什么泛型要设计成是不可变的呢?其实就是上面的那个问题:如果泛型不是不可变的,那么在方法参数里面,List<Object>可以加入其子类List<String>无法控制的类型,因为在方法里面我往List<Object>里面加入Integer是合法的,但是后续你往方法里面传入什么类型的参数我是不可控的啊,要是你传入了List<String>,还没有一种检查机制的话,那后果就不堪设想了。也就是说,如果泛型是协变的(可变的),那么List与List<Object>就一样了。

    以上是我对这块知识的一点理解,结合《Effictive Java》这本书,如果有哪里不正确的,还请大家指正。下一章我们来讲讲泛型和数组。

    相关文章

      网友评论

          本文标题:Java泛型——原生态类型与泛型

          本文链接:https://www.haomeiwen.com/subject/ncvlpftx.html