泛型

Wu Jun 2020-01-03 12:43:49
Categories: > > Tags:

1 泛型类

泛型类可以有多个类型变量,用尖括号(< >)括起来,并放在类名的后面。

通常用E表示集合的元素类型, K 和 V 分别表示表的关键字与值的类型。 T、U、S等表示“任意类型”。

泛型类可看作普通类的工厂。

public class Pair<T,U>{}
public class Pair<T>{
    private T first;
    public Pair(T first){ this.first=first;}
    public T getFirst(){ return first;}
    public void setFirst(T first){ this.first=first;}
}

2 泛型方法

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

只有声明了 <T> 的方法才是泛型方法

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

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

3 类型变量的限定

通过对类型变量 T 设置限定,对类型变量加以约束。

T 和绑定类型可以是类,也可以是接口。

<T extends Bounding Type >

一个类型变量或通配符可以有多个限定。

限定类型用“&”分隔,而逗号用来分隔类型变量。

<T extends Comparable & Serializable,U>

在 Java 的继承中,可以拥有多个接口超类型,但限定中至多有一个类。如果用一个类作为限定,它必须是限定列表中的第一个。

4 泛型代码和虚拟机

4.1 原始类型

虚拟机中没有泛型,泛型类型都会转换为相应普通类,即原始类型。

原始类型的名字就是删去类型参数后的泛型类型名。擦除类型变量,并替换为第一个限定类型(无限定的变量用Object)。

Pair<T> 的原始类型:

public class Pair{
	private Object first;
	public Pair(Object first){this.first=first;}
	public Object getFirst(){ return first; }
	public void setFirst(Object first){ this.first=first; }
}

4.2 类型转换

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

  1. 调用原始方法
  2. 将返回的 Object 对象进行强制类型转换

当存取一个泛型域时也要插入强制类型转换。

4.3 桥方法

当泛型方法被擦除后与多态发生冲突,编译器会在泛型方法类中生成一个桥方法。

//超类  
class Pair<T>{
	public void setSecond(T second){}
}
//子类  
class DateInterval extends Pair<Date>{
	public void setSecond(Date second){}
}
//子类被擦除后  
class DateInterval extends Pair{
	public void setSecond(Date second){ ... }
}
//还有从超类继承的方法  
public void setSecond(Object second)
//此时调用,多态冲突  
DateInterval interval=new DateInterval(...);
Pair<Date> pair=interval;//OK--assignment to superclass
pair.setSecond(aDate);
//编译器在DateInterval类中生成桥方法
public void setSecond(Object second){ setSecond((Date) second); }

5 约束与局限性

  1. 类型参数不支持基本类型
  2. 运行时类型检查只适用于原始类型
    instanceof、getClass 判断获取的都是原始类型
  3. 不能抛出或捕获泛型类的实例
    可在异常声明中
  4. 不能创建参数化类型的数组
    可以使用 ArrayList
  5. 不能实例化类型变量
    不能使用 new T(…), new T[…] 或 T.class 这样的表达式
  6. 泛型类的静态上下文中的类型变量无效
    不能在静态域或方法中引用类型变量,要重新定义
  7. 注意擦除后的冲突

6 通配符类型

6.1 子类型限定

<? extends Class>

? extends Employee getFirst()
void setFirst(? extends Employee)

可以调用 get,不能调用 set 方法。

6.2 超类型限定

<? super Manager>

? super Manager getFirst()
void setFirst(? super Manager)

可以调用 set,不能调用 get 方法。

6.2 无限定通配符

<?>

? getFirst()
void setFirst(?)

get 的返回值只能赋给一个 Object,set 方法不能被调。