首页 > 代码库 > 泛型学习笔记

泛型学习笔记

定义:参数化类型。使程序具有更好的可读性和安全性。

优点:

(泛型类,比如用于集合等容器类型时)

1.编译器进行类型检查,避免插入错误类型的对象;

2.输出时不需要类型的强制转换。

 

1.泛型类

可以有多个类型变量。

例:

public class Pair<T>{...}

public class Pair<K,V>{...}

2.泛型方法

既可以定义在普通类中,也可以定义在泛型类中。

例:

public static <T> T getMiddle(T[] a){

  return a[a.length/2];

}

调用时,可省略<T>类型参数。

3.类型变量的限定

实现方法:<T extends BoundingType>

表示T使绑定类型的子类型。其中,T和BoundingType可以是类,也可以是接口。且绑定类型可以有多个,中间用&连接,如<T extends Comparable & Serializable>。因为Java只能单继承,因此限定类型中至多只能有一个类,并且必须位于限定列表中的第一个。

4.泛型代码和虚拟机

虚拟机没有泛型类型对象——所有对象都属于普通类。即编译器生成的字节码是不包含泛型信息的,泛型类型信息在编译处理时被擦出——类型擦除

类型擦除

--定义:通过类型参数和并,将泛型类型实例关联到一份字节码上。

--原因:Java为引用类型系统,引用类型集合中的元素本质上都是一个个指针,没必要为每个类型都产生一份执行代码(浪费空间)。

--实现方法:

(1)将所有的泛型参数用最左边界(最顶级的父类型)类型替换;

(2)移除所有类型参数。

4.1 翻译泛型表达式

当程序调用泛型方法时,如果擦除返回类型,编译器将进行强制类型转换。

4.2 翻译泛型方法

例:

class DateInterval extends Pair<Date>{

  public void getSecond(Date second){

    if(second.compareTo(getFirst())>=0)

      super.setSecond(second);

  }

}

类型擦除后,变成

class DateInterval extends Pair{

  public void getSecond(Date second){

    if(second.compareTo(getFirst())>=0)

      super.setSecond(second);

  }

}

那么,DateInterval类中存在一个从Pair类中继承的方法:public void getSecond(Object date){...}。存在问题。

考虑:

DateInterval interval=new DateInterval();

Pair<Date> pair=interval;

pair.setSecond(aDate);

希望对于setSecond的调用具有多态性,因为pair引用DateInterval的对象,故应调用DateInterval中的setSecond方法。问题在于类型擦除与多态发生了冲突。因此,编译器会在DateInterval类中生成一个桥方法(用来保持多态):

public void setSecond(Object aDate){

  setSecond((Date) aDate);

}

5.Java泛型的约束与局限性

(1)不能用基本类型实例化参数类型

  例:Pair<double>//error;应为Pair<Double>

(2)运行时类型查询只适用于原始类型

  例:

     A)if(a instanceof Pair<T>)//error;只能测试a是否是任意类型的Pair

     B)Pair<String> p=(Pair<String>) a//warning; 只能测试a是否是任意类型的Pair

     C)getClass也总是返回原始类型

    Pair<String> sPair=...

    Pair<Employee> ePair=...

    if(sPair.getClass()==ePair.getClass())//true

(3)不能创建参数化类型的数组

  不能创建,如Pair<String>[] pArr=new Pair<String>[10];//error

      但是可以声明,如Pair<String>[] pArr;

      一种安全有效地方法是使用ArrayList,即ArrayList<Pair<String>>。

(4)不能实例化类型变量

(5)泛型类的静态上下文中类型变量无效

(6)不能抛出或捕获泛型类的实例

(7)注意擦除后的冲突

6.泛型类型的继承规则

注意泛型与数组的区别。

例如:

一个ArrayList<Manager>可转换为一个List<Manager>;

但是,一个ArrayList<Manager>不能转换为ArrayList<Employee>或List<Employee>。

7.通配符类型

7.1 子类型限定

Pair<? extends Employee>

? extends Employee getFirst()//合法,可赋值给Manager对象

void setFirst(? extends Employee)//不合法,编译器不能判断具体是什么类型

7.2 超类型限定

Pair<? super Manager>

? super Manager getFirst()//不合法,只能赋值给一个Object类型对象

void setFirst(? super Manager)//合法,编译器不知道具体类型,但是可以用任何Manager对象或子类型对象调用,但不能用Employee对象

小结:带有超类型限定通配符的可以向泛型对象写入,带有子类型限定通配符的可以从泛型对象读出。

7.3 无限定通配符

Pair<?>

? getFirst()//只能赋值给一个Object

void setFirst(?)//不能被调用,甚至不能被Object调用

Pair<?>与Pair的本质不同在于:可以用任意Object对象调用原始Pair类的setFirst方法。

泛型学习笔记