1.1 使用Object表示泛型
Java中的基本思想就是可以通过使用像Object这样适当的超类来实现泛型类。--《数据结构与算法分析 Java语言描述》
文中的超类也就是父类的意思。
使用Object这样特殊的类来实现泛型是用到了Object是所有类的父类这个基本思想,但是这种实现方法带来了两个问题:
1. 没有覆盖基本类型,因为基本类型不是引用类型,所以不能用类表示基本类型,因此Object不是基本类型的父类
2. 在使用泛型后的对象时需要强制转换,如果强转失败了也不会在编译时报错,只会在运行时报强转失败ClassCastException
这样Object在实际使用中就不是一个非常完美的实现泛型的一个方法,但是提供了一个实现泛型的基本思想,就是通过父类来实现泛型。请看例子
/**
* 使用Object实现泛型
*/
public class ObjectGeneric {
private Object storedValue;
public Object read(){
return storedValue;
}
public void write(Object value){
storedValue = value;
}
public static void main(String[] args) {
ObjectGeneric objectGeneric = new ObjectGeneric();
objectGeneric.write("23");
String val = (String)objectGeneric.read();
System.out.println(val);
}
}
1.2 使用接口类型表示泛型
当有多个类要在一个通用的方法里表示泛型时,Object来表示可能就显得捉襟见肘了,因为这个时候无法明确的知道用户到底需要拆箱为哪种类。例如,String和Shape类的数组要用同一个findMax函数来找出最大的元素,这时在实现findMax的时候可能需要随时要判断入参的类型,这对于泛型的意义(减少代码量)来说是毁灭性的。
因此这个时候需要找到String和Shape类的共同之处,抽象出一个接口,并在findMax里面用作入参来替代直接传入String或Shape类。findMax的核心在于元素间的比较,在jdk中有一个接口java.lang.Comparable是满足findMax需求的。
首先,定义一个实现了Comparable接口的抽象类Shape
public abstract class Shape implements Comparable{
protected Double area;
@Override
public int compareTo(Object o) {
Shape shape = (Shape)o;
return area.compareTo(shape.getArea());
}
public Double getArea() {
return area;
}
public void setArea(Double area) {
this.area = area;
}
}
然后派生出两个具体的Shape子类:Square,Circle
public class Square extends Shape{
public Square(double len){
area = len*len;
}
}
public class Circle extends Shape{
public Circle(double radius){
area = radius*radius*3.14;
}
}
最后是findMax函数
import static com.google.common.base.Preconditions.checkArgument;
public class InterfaceGeneric {
public static void main(String[] args) {
Shape[] sh = {
new Circle(3.0),
new Square(3.0)
};
String[] str = {
"John",
"Benjamin",
"Steve"
};
Comparable[] comparables = {
new Circle(3.0),
"Benjamin"
};
System.out.println(findMax(sh));
System.out.println(findMax(str));
System.out.println(findMax(comparables));
}
public static Comparable findMax(Comparable[] arr){
checkArgument(arr.length>0);
int max = 0;
for(int i = 0 ;i<arr.length;i++){
if(arr[i].compareTo(arr[max])>0){
max = i;
}
}
return arr[max];
}
}
在findMax函数中,因为已知入参是实现了Comparable接口的类,因此可以不用转换直接使用compareTo函数,这是利用了接口的特性来实现的泛型。
System.out.println(findMax(comparables))会抛出ClassCastException异常,这是因为在compareTo中拆箱时无法将String类强转为Shape类导致的。
以上两种方式都存在编译不会报错,但是会在运行时报错的问题,这样就导致程序是不可控的,因为无法提前预知程序哪里会抛RuntimeException,所以需要一种泛型方法来强制使得异常能够出现在编译阶段而不是运行时。
1.3 利用Java5泛型特性实现泛型构件
在java5中引入了泛型类的概念,通过<>运算符实现泛型。将第一个例子用<>运算符实现如下:
public class DiamondGeneric<AnyType> {
private AnyType storedValue;
public AnyType read(){
return storedValue;
}
public void write(AnyType value){
storedValue = value;
}
public static void main(String[] args) {
DiamondGeneric<String> val = new DiamondGeneric<>();
val.write("this is a test");
System.out.println(val.read());
}
}
在这里DiamondGeneric<String>操作限制read,write函数入参出参的类型只能为String,避免了运行时强转带来的Exception。
在这里还用到了Java7增加的一种新的语言特性,菱形运算符:new DiamondGeneric<>(),在前面已经指定AnyType为String,因此在后面不必再指定类型。
1.4 带有限制的通配符
想一种复杂的状况,如果一个接口的多态实现需要在一个通用方法做同样的操作,例如,Shape接口有一个area方法,有一个实现Circle,一个实现Square,需要一个findAreaCount来计算出Shape集合的面积总数,那么需要在<>里面加入怎样的限制才能做到?
答案是Collection<? extends Shape>
public class DiamondInterfaceGeneric {
public static void main(String[] args) {
List sh = new ArrayList();
sh.add(new Circle(3.0));
sh.add(new Square(3.0));
System.out.println(totalArea(sh));
}
public static double totalArea(Collection<? extends Shape> arr){
double total = 0;
for(Shape s : arr){
if(s!=null)
total+= s.getArea();
}
return total;
}
}
1.5 类型限界
在考虑一个更加极端的状况,Comparable是泛型的,指定的类型为Shape,也即Comparable<Shape>,同时Shape继承了Comparable<Shape>,也存在Square类或Circle类实现了Shape接口,那么这时候该如何表示Square extends Comparable<Shape>或Circle extends Comparable<Shape>这种类呢?
答案是AnyType extends Comparable<? super AnyType>
public abstract class ShapeGeneric implements Comparable<ShapeGeneric>{
protected Double area;
@Override
public int compareTo(ShapeGeneric o) {
return area.compareTo(o.area);
}
public Double getArea() {
return area;
}
public void setArea(Double area) {
this.area = area;
}
}
public class CircleGeneric extends ShapeGeneric{
public CircleGeneric(double radius){
area = radius*radius*3.14;
}
}
public class SquareGeneric extends ShapeGeneric{
public SquareGeneric(double len){
area = len*len;
}
}
public class ComplexInterfaceGeneric {
public static void main(String[] args) {
ShapeGeneric[] sh = {
new CircleGeneric(3.0),
new SquareGeneric(3.0)
};
System.out.println(findMax(sh));
}
public static <AnyType extends Comparable<? super AnyType>> AnyType findMax(AnyType[] arr){
checkArgument(arr.length>0);
int max = 0;
for(int i = 0 ;i<arr.length;i++){
if(arr[i].compareTo(arr[max])>0){
max = i;
}
}
return arr[max];
}
}