核心技术:泛型
为什么要使用泛型
泛型程序设计(Generic programming) 意味着编写的代码可以被很多不同类型的对象所重用。
类型参数( type parameters )。ArrayList 类有一个类型参数用来指示元素的类型:
ArrayList<String> files = new ArrayList<String>();
- 在Java SE 7 及以后的版本中, 构造函数中可以省略泛型类型:
ArrayList<String> files = new ArrayList<>();
定义简单泛型类
一个泛型类( generic class ) 就是具有一个或多个类型变量的类。
- 换句话说,泛型类可看作普通类的工厂。
泛型方法
- 类型变量放在修饰符(这里是public static ) 的后面,返回类型的前面。
class ArrayAlg
{
public static <T> T getMiddle(T... a)
{
return a[a.length / 2];
}
}
泛型变量的限定
是将T 限制为实现了Comparable 接口(只含一个方法compareTo 的标准接口)的类:
public static <T extends Coiparab1e> T min(T[] a) . . .
- 使用关键字extends 而不是implements
- T 和绑定类型可以是类, 也可以是接口
- 一个类型变量或通配符可以有多个限定:
T extends Comparable & Serializable
- 限定类型用“&”分隔,而逗号用来分隔类型变量。
泛型代码和虚拟机
类型擦除
无论何时定义一个泛型类型, 都自动提供了一个相应的原始类型( raw type )。
- 原始类型的名字就是删去类型参数后的泛型类型名。
- 擦除(erased) 类型变量, 并替换为限定类型(无限定的变量用Object)。
如:
public class Pair<T>
{
private T first;
private T second;
public Pair() { first = null ; second = null ; }
public Pair(T first , T second) { this,first = first; this.second = second; }
public T getFirst() { return first; }
public T getSecond() { return second; }
public void setFirst (T newValue) { first = newValue; }
public void setSecond(T newValue) { second = newValue; }
}
擦除为:
public class Pair
{
private Object first;
private Object second;
public Pair() { first = null ; second = null ; }
public Pair(Object first, Object second)
{
this,first = first;
this.second = second;
}
public Object getFirst() { return first; }
public Object getSecond() { return second; }
public void setFirst(Object newValue) { first = newValue; }
public void setSecond(Object newValue) { second = newValue; }
}
- 原始类型用第一个限定的类型变量来替换, 如果没有给定限定就用Object 替换。
例:
public class Interval <T extends Comparable & Serializable〉implements Serializable
{
private T lower;
private T upper;
...
public Interval (T first , T second)
{
if (first .compareTo(second) <= 0) { lower = first ; upper = second; }
else { lower = second; upper = first; }
}
}
擦除为:
public class Interval implements Serializable
{
private Comparable lower;
private Coiparable upper;
...
public Interval (Coiparable first, Coiparable second) { . . . }
}
翻译泛型表达式
对于如下表达式:
Pair<Employee> buddies = . .;
Employee buddy = buddies.getFirst();
编译器把这个方法调用翻译为两条虚拟机指令:
- 对原始方法Pair.getFirst 的调用。
- 将返回的Object 类型强制转换为Employee 类型。
翻译泛型方法
类型擦除也会出现在泛型方法中:
public static <T extends Comparable〉T min(T[] a)
擦除为:
public static Comparable min(Comparable[] a)
方法的擦除带来了两个复杂问题。如:
class Datelnterval extends Pair<LocalDate>
{
public void setSecond (Local Date second)
{
if (second.compareTo(getFi rst()) >= 0)
super. setSecond (second);
}
...
}
这个类擦除后变成:
class Dateinterval extends Pair// after erasure
{
public void setSecond (Local Date second) { . . . }
...
}
而Datelnterval同时存在一个从Pair继承的setSecond方法,即:
pub1ic void setSecond (Object second)
考虑下面的语句序列:
Datelnterval interval = new Datelnterval (...) ;
Pair<LocalDate> pair= interval; // OK--assignment to superclass
pair.setSecond (aDate);
在这里,类型擦除与多态发生了冲突。要解决这个问题,就需要编译器在Datelnterval类中生成一个桥方法(bridgemethod):
public void setSecond(Object second)
{
setSecond((Date) second);
}
有关Java 泛型转换的事实:
- 虚拟机中没有泛型, 只有普通的类和方法。
- 所有的类型参数都用它们的限定类型替换。
- 桥方法被合成来保持多态。
- 为保持类型安全性,必要时插人强制类型转换。