泛型

  • 泛型程序设计意味着编写的代码可以被很多不同类型的对象所重用
  • 泛型类是普通类引用了一个类型变量,用尖括号(<>)括起来,并放在类名后面。泛型类可以有多个类型变量
  • 类型变量常使用大写形式,且比较短,在java中使用变量E表示集合的元素类型,K,V表示关键字与值的类型,T,U,S表示任意类型
  • 泛型类可以看做普通类的工厂
  • 泛型方法中的类型变量修饰符的后面,返回类型的前面
  • 泛型方法可以定义在普通类中,也可以定义在泛型类中,当调用泛型方法时,在方法名前放入具体的类型
  • <T extends Object(类名)>,表示T应该是绑定类型的子类型,T和绑定类型可以是类,可以是接口
  • 一个类型变量或者通配符可以有多个限定,限定类型要用&连接,如T extends Comparable & Serializable
  • 在java的继承中,可以根据需要拥有多个接口类型,但限定中最多有一个类,如果有用类作为限定,则其必须为限定列表中的第一个
  • 强调一点,虚拟机中没有泛型类对象,所有泛型类对象都属于普通类,通常会通过类型擦除,如果没有限定类型,则会用Object修饰,有的话,通常会使用限定列表中的第一个限定的类型变量来替换
  • 当然,如果有必要使用其他限定的类型变量,编译器会在必要的时刻强制转换成相应的类型
  • 泛型方法类似,当调用泛型方法时,编译器会把方法调用翻译成两条指令,1、对原始方法的调用 2、将返回类型Object强制转换为相应的类型
  • 方法擦除可能会与多态发生冲突,如子类继承一个父类,父类有方法 setNumber(T number),子类中有方法setNumber(Integer number),当擦除后,会有两个不同的方法,方法签名不同,这时编译器会在子类中生成一个桥方法,(这里不解释,可以百度),当调用方法时,实际上调用的就是桥方法
  • 桥方法有趣的是,在无参数且有返回值时,方法签名就相同,按理说这时不合理的,但在虚拟机中可以根据返回类型确定一个方法。
  • 桥方法不仅只在泛型中使用,如一个方法覆盖另一个方法时可以指定一个更严格的返回类型,就会生成一个桥方法,通常是继承父类的方法和子类的方法属于重载关系,或者返回类型不一致时,会生成一个桥方法
  • 上述整理下:
  1. 虚拟机中没有泛型,只有普通类和方法
  2. 所有的类型参数都会用他们的限定类型替换
  3. 桥方法被合成用来保持多态
  4. 为保持类型安全性,必要时插入强制转换类型
  • 设计泛型时,主要目标是允许泛型代码和遗留代码之间相互操作(这里就不在详述,具体百度)
  • 泛型的局限和约束性:
  1. 不能用基本类型实例化泛型参数,由于泛型类型通常是类和接口,而基本类型不是类
  2. 运行时类型查询只适用于原始类型:a instanceof Pair (error,错误),PairStringPair…,StringPair.getClass() == Employee.getClass()//true ,相等
  3. 不能创建参数化类型数组,注意是参数化。,如Pair[] table =new Pair[10]//error,这是因为类型擦除后可以存储不一致类型的变量而不报异常,如a[0]=Pair…,a[1} =Pair…,但是据说可以声明参数化类型数组,且可以Pair[] str = new Pair[10]来创建,
  4. 不能实例化类型变量,如first =new T()//error,解决方法是提供一个构造器表达式(函数式接口)或者通过反射调用Class.newInstance。因为不希望new Object。
  5. 不能构造泛型数组,如T[] a=new T[10]//error,这是由于类型擦除后会成为限定类型数组,可能会永远都是这个数组,与泛型的本质不合理,因为是得到任意类型,而不是单一类型,也可能导致一些预料不到的错误
  6. 泛型类的静态上下文中类型变量无效,这是因为变量是类共享的,如果在实例化时Pair,Pair则变量类型是什么类型的呢?
  7. 不能抛出或者捕获泛型类的实例,因为在捕获过程中,若果泛型擦除后,都为Pair,则若有俩个catch,则两个都一样的,且有可能在擦除后,在前面的可能是父类异常,后面才是子类异常,这也不合理
  8. 可以消除对首查异常的检查
  9. 注意擦除后的冲突
  • 永远可以将参数化类型转换成原始类型,如Pair 是原始类型Pair的子类型,而原始类型的其他子类型相互之间没有关系。
  • Pair<? extends Employee>表示任何pair类型,但他的参数类型必须是Employee的子类,不过这种类型中Set方法编译器会报错,因为只知道传递一个Employee的子类,但是却不知道具体类型(因为如果用Employee接受,但在后面过程中无法向下转型)。与set相反的是get方法是合法的,因为将返回值赋值给一个Employee是合法的。
  • Pair<? super Manager>,与上述相反,参数类型必须是Manager的超类,且只能给方法提供参数(set值只能是当前类或其子类型,因为可以向上转型,但父类不能向下转型),但不能给返回值
  • 泛型类型擦除后,是无法得知自己的原始信息,因为都是基本类,不过还是保留有一些痕迹,