美文网首页
深入理解Java泛型

深入理解Java泛型

作者: 俊杰的笔记 | 来源:发表于2017-05-18 11:49 被阅读189次

    Java类型系统

    参考:http://www.infoq.com/cn/articles/cf-java-generics

    在Java中,大家比较熟悉的是通过继承机制而产生的类型体系结构。比如String继承自Object。根据Liskov替换原则,子类是可以替换父类的。当需要Object类的引用的时候,如果传入一个String对象是没有任何问题的。但是反过来的话,即用父类的引用替换子类引用的时候,就需要进行强制类型转换。编译器并不能保证运行时刻这种转换一定是合法的。这种自动的子类替换父类的类型转换机制,对于数组也是适用的。 String[]可以替换Object[]。但是泛型的引入,对于这个类型系统产生了一定的影响。正如前面提到的List<String>是不能替换掉List<Object>的。
    引入泛型之后的类型系统增加了两个维度:一个是类型参数自身的继承体系结构,另外一个是泛型类或接口自身的继承体系结构。第一个指的是对于 List<String>和List<Object>这样的情况,类型参数String是继承自Object的。而第二种指的是 List接口继承自Collection接口。对于这个类型系统,有如下的一些规则:
    相同类型参数的泛型类的关系取决于泛型类自身的继承体系结构。即List<String>是Collection<String> 的子类型,List<String>可以替换Collection<String>。这种情况也适用于带有上下界的类型声明。
    当泛型类的类型声明中使用了通配符的时候, 其子类型可以在两个维度上分别展开。如对Collection<? extends Number>来说,其子类型可以在Collection这个维度上展开,即List<? extends Number>和Set<? extends Number>等;也可以在Number这个层次上展开,即Collection<Double>和 Collection<Integer>等。如此循环下去,ArrayList<Long>和 HashSet<Double>等也都算是Collection<? extends Number>的子类型。
    如果泛型类中包含多个类型参数,则对于每个类型参数分别应用上面的规则。

    总结:
    使用了泛型参数的类叫泛型类,下面简称“类”和“泛型参数”。

    1. 当泛型参数相同时, 按类的继承体系。如List<String>是Collection<String>的子类。
    2. 泛型参数无通配符时,看做不可变的泛型参数。即使类相同,也无任何继承关系。如:List<Integer>和List<Number>没有关系。
    3. 泛型参数有通配符时,参数相同时,看类的关系;类相同时,看参数的关系。都不相同时,两者都要看。如ArrayList<Long>和 HashSet<Double>等也都算是Collection<? extends Number>的子类型.

    关于extends和super

    假定现有A和B两个类, B是A的子类,则
    List<B>是List<? extends A>的子类;
    List<A>是List<? super B>的子类(注意这一条,参数和类的关系正好相反)

    • 如果一个类只需要读取,用extends
      从List<? extends T>中读取,必然可以判定元素为T类型。T t = list.get(0);
      每一个元素必然是T类型的,所以可以get并用T引用。
      但是,写入时,编译器不知道list中元素具体是哪个子类型,写入另一种子类对象 不能保证元素一致性。

    • 如果一个类只需要写入,用super;
      写入List<? super T>时,编译器可以确定写入的元素只要是T类型及其子类即可。
      但读取时,编译器不知道具体是哪个父类,只能用Object t = list.get(0);

    • 如果既要读又要写,不要用通配符。

    相关文章

      网友评论

          本文标题:深入理解Java泛型

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