泛型

作者: 于无声处写写写 | 来源:发表于2018-07-15 10:30 被阅读21次

    只标记重要的部分


    什么是泛型?

    泛型即参数化类型,参数化类型就是说将类型由原来具体给定的某一个类型参数化,也就是说现在的类型是一个参数,由这个参数决定到底是什么类型。
    JavaSE7及以后,构造函数中可以省略泛型类型:

    ArrayList<String> list=new ArrayList<>();
    

    类型变量放在修饰符的后面,返回类型的前面。

    class ArrayAlg
    {
        public static <T> T getMiddle(T...a)
        {
            return a[a.length/2];
        }
    }
    

    类型变量的限定

    public static <T extends Comparable> T min(T[] a)
    

    表示T是Comparable类型的子类型,一个类型变量或通配符可以有多个限定
    限定类型用&分隔,逗号用来分隔类型变量
    可以有多个接口超类型,但是限定中至多有一个类。
    如果用一个类作为限定,它必须是限定列表中的第一个。


    泛型代码和虚拟机

    虚拟机没有泛型类型对象---所有对象都属于普通类
    在虚拟机中,无论何时定义一个泛型类型,都自动提供了一个相应的原始类型,原始类型的名字就是删去类型参数后的泛型类类名。
    Pair<T>的原始类型为Pair

    Java泛型转换的事实:

    • 虚拟机中没有泛型,只有普通的类和方法。
    • 所有的类型参数都用他们的限定类型替换
    • 桥方法被合成来保持多态
    • 为保持类型安全性,必要时插入强制类型转换

    使用泛型类时,虽然传入了不同的泛型实参,但并没有真正意义上生成不同的类型,传入不同泛型实参的泛型类在内存上只有一个,即还是原来最基本的类型,举个栗子也就是Pair<T>的原始类型为Pair,原因在于java泛型这一概念提出的目的,导致其只是作用于代码编译阶段,在编译过程中,对于正确检验泛型结果后,会将泛型的相关信息擦除,也就是说成功编译过后的class文件是不包含任何泛型信息的,泛型信息不会进入到运行阶段,不会进入虚拟机运行阶段。

    • 对此总结成一句话,泛型类型在逻辑上可以看成是多个不同的类型,实际上都是相同的基本类型。

    约束与局限性

    • 不能用基本类型实例化类型参数,因此没有Pair<double>,只有Pair<Double>
    • 虚拟机中的对象总有一个特定的非泛型类型。因此所有的类型查询只产生原始类型。
    • 试图查询一个对象是否属于某个泛型类型时,倘若使用instanceof会得到一个编译器错误,如果使用强制类型转换会得到一个警告。
    • 不能创建参数化类型的数组
    • 泛型类的静态上下文中类型变量无效
    public class Singleton<T>
    {
      private static T singleInstance();
    }
    

    上述是无效的

    • 既不能抛出也不能捕获泛型类对象,甚至泛型类扩展Throwable都是不合法的
    public class Problem<T> extends Exception
    
    • 一个类或者类型变量不能同时成为两个接口类型的子类,而这两个接口是同一接口的不同参数化。如下
    class Employee implements Comparable<Employee>{}
    class Manager extends Employee implements Comparable<Manager>{}
    

    Manager会实现Comparable<Employee>和Comparable<Manager>,这是同一接口的不同参数化。

    • 带有超类型限定的通配符可以向泛型对象写入,带有子类型限定的通配符可以从泛型对象读取。
    • 通配符不是类型变量,因此不能在代码中使用“?”作为一种类型
    • 此处’?’是类型实参,而不是类型形参 !再直白点的意思就是,此处的?和Number、String、Integer一样都是一种实际的类型,可以把?看成所有类型的父类。是一种真实的类型。

    在逻辑上Box<Number>不能视为Box<Integer>的父类。因为在编译阶段以后会进行类型擦除,都成了Box类

    • 只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。

      • 这才是一个真正的泛型方法。
      • 首先在public与返回值之间的<T>必不可少,这表明这是一个泛型方法,并且声明了一个泛型T
      • 这个T可以出现在这个泛型方法的任意位置.
      • 泛型的数量也可以为任意多个
      • 如:public <T,K> K showKeyName(Generic<T> container){}
    • 类中的泛型方法
      //在泛型类中声明了一个泛型方法,使用泛型E,这种泛型E可以为任意类型。可以类型与T相同,也可以不同。
      //由于泛型方法在声明的时候会声明泛型<E>,因此即使在泛型类中并未声明泛型,编译器也能够正确识别泛型方法中识别的泛型。
      public <E> void show_3(E t){
      System.out.println(t.toString());
      }
      在泛型类中声明了一个泛型方法,使用泛型T,注意这个T是一种全新的类型,可以与泛型类中声明的T不是同一种类型。
      public <T> void show_2(T t){
      System.out.println(t.toString());
      }

    • //在泛型方法中添加上下边界限制的时候,必须在权限声明与返回值之间的<T>上添加上下边界,即在泛型声明的时候添加
      //public <T> T showKeyName(Generic<T extends Number> container),编译器会报错:"Unexpected bound"

    • 下面的声明则不会报错
      public <T extends Number> T showKeyName(Generic<T> container){

    相关文章

      网友评论

        本文标题:泛型

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