1.4 实现泛型构件
泛型机制(generic mechanism):如果除去对象的基本类型外,实现方法时相同的,那么我们就可以用泛型实现(generic implementation)来描述这种基本功能。
1.4.1 使用Object表示泛型
Java中的基本思想就是可以通过使用像Object这样适当的超类来实现泛型类,下图所示MemoryCell
类就是这样一个例子。
// MemoryCell class
// Object read() -->Returns the stored value
// void write(Object x) -->x is stored
public class MemoryCell {
public Object read(){ return storedValue;}
public void write(Object x){ storedValue = x;}
private Object storedValue;
}
使用泛型MemoryCell
类
public class TestMemoryCell {
public static void main(String[] args){
MemoryCell m = new MemoryCell();
m.write("37");
String val = (String)m.read();
System.out.println("Contents are: "+val);
}
}
当我们这么写时,有两个地方值得考虑。
(1) 改main方法将字符串“37”写到MemoryCell
对象中,然后又从MemoryCell
对象读出,为了访问这种对象的一个特定方法,必须要强制转换成正确的类型。
(2) 不能适用基本类型。只有引用类型能够与Object
相容。
1.4.2 基本类型的包装(包装类,wrapper class)
在Java中我们已经看到,虽然每一个引用类型都和Object
相容,但是,8中基本类型却不能。Java为每一种基本类型都提供了一个包装类,每一个包装对象都是不可变的(就是说它的状态不能改变),存储一种当该对象呗构建时所设置的原值,并提供一种方法以得到改值,包装类也包含不少的静态实用方法。
基本类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
1.4.3 使用接口类型表示泛型类
只有在使用Object
类中已有的那些方法所执行的操作的时候,才能使用Object
作为泛型类型来工作。
例如,考虑在由一些项组成的数组中找出最大项的问题。基本的代码是类型无关的,但是的确需要一种能力来比较任意两个对象的大小。因此,不能直接找出Object
的数组中的最大元素。最简单的想法就是找出Comparable
数组中的最大元,可以使用compareTo
方法,下面代码做的就是这项工作。
class FindMaxDemo{
// Return max item in arr.
// Precondition: arr.length>0
public static Comaparable findMax(Comparable[] arr){
int maxIndex = 0;
for(int i=1;i<arr.length;i++){
if(arr[i].compareTo(arr[maxIndex]) > 0)
maxIndex = i;
return arr[maxIndex];
}
}
// Test findMax on Shape and String objects
public static void main(String[] args){
Shape[] sh1 = {new Circle(2.0), new Square(3.0),
new Rectangle(3.0, 4.0)};
String[] st1 = {"Joe", "Bob", "Bill", "Zeke"};
System.out.println(findMax(sh1));
System.out.println(findMax(st1));
}
}
(1) 只有实现Comparable
接口的那些对象才能作为Comparable
数组的元素被传递。仅有compareTo
方法但并未宣称实现Comparable
接口的对象不是Comparable
的,不具有必需的IS-A关系。
(2) 若Comparable
数组有两个不相容的对象(如, 一个String和一个Shape),那么CompareTo
方法将抛出异常ClassCastException
。
(3) 基本类型不能作为Comparable
传递,但是包装类可以,因为它们实现了Comparable
接口。
(4) 接口究竟是不是标准的库接口不是必需的。
(5) 这个方案不是总能够行得通,因为有时候宣称一个类实现所需的接口是不可能的。例如,一个类可能是库中的类,而接口确实用户定义的接口。如果一个类是final类,那么我们就不可能扩展它以创建一个新的类。
1.4.4 数组类型的兼容性
设Employee
IS-A Person
,那么是否意味着Employee[]
IS-APerson[]
呢?即如果一个例程接受Person[]
作为参数,那么能不能把Employee[]
作为参数来传递呢?
假设除了Employee
以外,还有Student
IS-A Person
,并设Employee[]
是和Student[]
类型兼容的,考虑下面两条赋值语句。
Person[] arr = new Employee[5]; // 编译:arrays are compatible
arr[0] = new Student(...); // 编译: Student IS-A person
两句都编译, 而arr[0]
实际上是引用一个Employee
,可是Student
IS-NOT-A Employee
,这样就产生了类型混乱。运行时系统(runtime system)不能抛出ClassCastException
异常,因此不存在类型转换。
避免这种问题的最容易的方法时指定这些数组不是兼容的。可是在Java中数组却是类型兼容的,这叫做协变数组类型(covariant array type)。每个数组都说明了它所允许存储的对象的类型。如果将一个不兼容的类型插入到数组中,虚拟机将抛出一个ArrayStoreException
异常。