您的位置:首页 > 编程语言 > Java开发

Java核心技术学习笔记之四——泛型程序设计

2016-03-07 13:52 330 查看


泛型程序设计(Generic programming)意味着编写的代码可以被很多不同类型的对象所重用。

例如,我们并不希望为聚集String和File对象分别设计不同的类。实际上,也不需要这样做,因为一个ArrayList类可以聚集任何类型的对象。

在Java SE 7及以后的版本中,构造函数中可以省略泛型类型:ArrayList<String>
files = new ArrayList<>();省略的类型可以从变量的类型推断得出。

在Java中增加泛型之前,泛型程序设计是用继承实现的。ArrayList类只维护一个Object引用的数组:

public
class
ArrayList{

private Object[]
elementData;

. . .

public Object get(int
i){...}

public
void add(Object o){...}

}

这样的实现有两个问题。当获取一个值时必须进行强制类型转换。

ArrayList files = new
ArrayList();

...

String filename = (String)files.get(0);

此外,这里没有错误检查,可以向数组添加任何类的对象。

files.add(newFike(“…”));

对于这个调用,编译和运行都不会出错,然而在其他地方,如果将get的结果强制类型转换为String类型,就会产生一个错误。

泛型提供了一个更好的解决方案:类型参数(type parameters)。ArrayList类有一个类型参数来指示元素的类型:

ArrayList<String>files = new ArrayList<String>();

人们一看就知道这个数组中包含的是String对象。

编译器也能很好地利用这个信息。当调用get方法的时候,不需要进行强制类型转换,编译器就知道返回值类型为String,而不是Object。

这将比使用Object类型的参数安全一些。现在,编译器可以进行检查,避免插入错误类型的对象。类型参数的魅力在于:使得程序具有更好的可读性和安全性。

泛型的使用有泛型类、泛型方法、

类型变量的限定

有时,类或方法需要对类型变量加以约束。public static <T extendsComparable> T min(T[] a)…

一个类型变量或通配符可以有多个限定,例如:T extends Comparable &Serializable限定类型使用&分隔,而逗号用来分隔类型变量。

12.5
泛型代码和虚拟机

虚拟机没有泛型类型对象——多有对象都属于普通类。在泛型实现的早期版本中,设置能够使用泛型的程序编译为在1.0虚拟机上运行的类文件!这个向后兼容性在泛型开发的后期被放弃了。

无论合适定义一个泛型类型,都自动提供了一个相应的原始类型(raw type)。原始类型的名字就是删去类型参数后的泛型类型名。擦除(erased)类型变量,并替换为限定类型(无限定的变量用Object)

例如,Pari<T>的原始类型如下所示;

public
class Pair {

private Object
first;

private Object
second;

public Object getFirst() {return
first;}

public
void setFirst(Object first) {this.first
= first;}

public Object getSecond() {return
second;}

public
void setSecond(Object second) {this.second
= second;}

public Pair(Object first, Object second) {

this.first
= first;

this.second
= second;

}

}

因为T是一个无限定的变量,直接用Object替换。

结果是一个普通的类,就好像泛型引入Java语言之前已经实现的那样。

在程序中可以包含不同类型的Pair,例如,Pair<String>或Pair<GregorianCalendar>而擦除类型后就变成原始的Pair类型了。

原始类型用第一个限定类型变量来替换,如果没有给定限定就用Object替换。例如,类Pair<T>中的类型变量没有显式的限定,因此,原始类型用Object替换T。假定声明了一个不同的类型。

pubic class
Interval<Textends
Comparable &Serializable> implements Serializable{

private T
lower;

private T
upper;

//...

public Interval(T lower, T upper) {

super();

this.lower
= lower;

this.upper
= upper;

}

}

原始类型Interval如下所示:

pubic class
Intervalimplements Serializable{

private
Comparable lower;

private
Comparable upper;

//...

public Interval(Comparable lower,
Comparable upper) {

super();

this.lower
= lower;

this.upper
= upper;

}

}

12.5.1
翻译泛型表达式

当程序调用泛型方法时,如果擦除返回类型,编译器插入强制类型转换。例如下面这个语句序列

Pair<Employee> buddies = …

Employee buddy = buddies.getFirst();

擦除getFirst()的返回类型后将返回Object类型。编译器自动插入Employee的强制类型转换。也就是说,编译器把这个方法调用翻译为两条虚拟机指令:

对原始方法Pair.getFirst()的调用。

将返回的Object类型强制转换为Employee类型。

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

12.6
约束域局限性

12.6.1
不能用基本类型实例化类型参数(可以使用对应的包装类进行替换)

12.6.2
运行时类型查询只适用于原始类型

12.6.3
不能创建参数化类型的数组

12.6.5
不能实例化类型变量

12.6.6
泛型类的静态上下文中类型变量无效

12.6.7
不能抛出或捕获泛型类的实例

12.6.8
注意擦除后的冲突

12.7
泛型类的继承规则

在使用泛型类时,需要了解一些有关继承和子类型的准则。无论S与T有什么关系,通常Pair<S>与Pair<T>之间没有任何关系。

12.8
通配符类型

Pair<?ectends Employee>表示任何泛型Pair类型,它的类型参数是Employee的子类,如Pair<Manager>。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: