JAVA基础之泛型程序设计《二》
1. 泛型代码与虚拟机
1.1 类型擦除
无论何时定义一个泛型类,虚拟机都会提供一个相应的原始类型。这个原始类型名就是去掉类型参数后的的泛型类型名。类型变量会被擦除,并替换为限定类型(无限定类型则替换为Object)。如果有多个限定类型则选择第一个
// 泛型类
class Animal<T>{
private T obj;
public T getObj() {
return obj;
}
}
// 经过虚拟机转换
class Animal{
private Object obj;
public Object getObj() {
return obj;
}
}
1.2 转换泛型表达式
编写一个泛型方法,并且返回类型是类型变量,调用时,虚拟机会擦除返回类型,这时编译器会插入强制类型转换。如下
Pir<Employee> buddies = ...;
Employee buddy = buddies.getFirst();
getFirst()
获取 First 字段。类型擦除时,返回类型变为Object,编译器会自动插入Employee的强制类型转换。
2.3 转换泛型方法
类型擦除也会出现在泛型方法中。如果有限定类型则由限定类型代替
泛型方法:
public static <T extend Comparable> T min(T[] a)
擦除之后:
public static Comparable min(Comparable[] a)
由于擦除类型变量之后,泛型方法返回类型会变成Object,但是当一个类继承一个泛型类时,调用超类的方法,且这个超类的这个方法参数类型为类型变量,子类为具体类型变量。擦除之后,超类的这个方法参数类型 T 变成 Object 而子类调用超类该方法传入的参数为其它类型的参数。此时系统会合成乔方法来解决该问题。如下例题
package priv.zsl.template;
/**
* @author m1767
*/
@SuppressWarnings("all")
public class InheritT {
public static void main(String[] args) {
Animal animal = new Animal();
Biology<Mouse> biology = animal;
Mouse mouse = new Mouse();
biology.setName(mouse);
System.out.println(biology);
}
}
@SuppressWarnings("All")
class Animal extends Biology<Mouse>{
public void setName(Mouse mouse) {
super.setName(mouse);
}
}
@SuppressWarnings("All")
class Biology<T> {
private T name;
public void setName(T animal) {
this.name = animal;
System.out.println(this.name);
}
}
class Mouse {
@Override
public String toString() {
return super.toString();
}
}
运行结果
//priv.zsl.template.Mouse@14ae5a5
//priv.zsl.template.Animal@7f31245a
可见输出没有错误,正常输出。
查看bytecode:
// class version 52.0 (52)
// access flags 0x20
// signature Lpriv/zsl/template/Biology<Lpriv/zsl/template/Mouse;>;
// declaration: priv/zsl/template/Animal extends priv.zsl.template.Biology<priv.zsl.template.Mouse>
class priv/zsl/template/Animal extends priv/zsl/template/Biology {
// compiled from: InheritT.java
// access flags 0x0
<init>()V
L0
LINENUMBER 18 L0
ALOAD 0
INVOKESPECIAL priv/zsl/template/Biology.<init> ()V
RETURN
L1
LOCALVARIABLE this Lpriv/zsl/template/Animal; L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
// access flags 0x1
public setName(Lpriv/zsl/template/Mouse;)V
L0
LINENUMBER 21 L0
ALOAD 0
ALOAD 1
INVOKESPECIAL priv/zsl/template/Biology.setName (Ljava/lang/Object;)V
L1
LINENUMBER 22 L1
RETURN
L2
LOCALVARIABLE this Lpriv/zsl/template/Animal; L0 L2 0
LOCALVARIABLE mouse Lpriv/zsl/template/Mouse; L0 L2 1
MAXSTACK = 2
MAXLOCALS = 2
// access flags 0x1041
//系统生成的乔方法。
public synthetic bridge setName(Ljava/lang/Object;)V
L0
LINENUMBER 17 L0
ALOAD 0
ALOAD 1
CHECKCAST priv/zsl/template/Mouse
INVOKEVIRTUAL priv/zsl/template/Animal.setName (Lpriv/zsl/template/Mouse;)V
RETURN
L1
LOCALVARIABLE this Lpriv/zsl/template/Animal; L0 L1 0
MAXSTACK = 2
MAXLOCALS = 2
}
所以泛型方法在类的继承中,系统会自动生成桥方法:
所以子类具有俩个同样的方法。
public void setName(Mouse mouse) {
......
}
public void setName(Object ...) {
....
}
所以泛型方法在类的继承中,系统会自动生成桥方法:
所以子类具有俩个同样的方法。
public void setName(Mouse mouse) {
......
}
public void setName(Object ...) {
....
}
由于虚拟机可以通过返回类型和参数类型不一样来识别俩个同名方法,但是在编写java方法时不合法。