大可制作:QQ群:31564239(asp|jsp|php|mysql)

Java Gossip: 定义泛型类

当您定义类时,发现到好几个类的逻辑其实都相同,就只是当中所涉及的类型不一样时,使用复制、贴上、取代的功能来撰写程序只是让您增加不必要的文件管理困扰。

由于Java中所有定义的类,都以Object为最上层的父类,所以在 J2SE 5.0 之前,Java程序设计人员可以使用Object来解决上面这样的需求,为了让定义出来的类可以更加通用(Generic),传入的值或传回的对象都是以Object为主,当您要取出这些对象来使用时,必须记得将接口转换为原来的类型,这样才可以操作对象上的方法。

然而使用Object来撰写泛型类(Generic Class)留下了一个问题,因为您必须要转换接口,粗心的程序设计人员往往会忘了要作这个动作,或者是转换接口时用错了类型(像是该用Boolean却 用了Integer),要命的是,语法上是可以的,所以编译器检查不出错误,真正的错误要在运行时才会发生,这时恼人的ClassCastException就会出来搞怪,在使用Object设计泛型程序时,程序人员要再细心一些、小心一些。

在J2SE 5.0之后,提出了针对泛型(Generics)设计的解决方案,要定义一个简单的泛型类是简单的,直接来看个例子:

  • GenericFoo.java
public class GenericFoo<T> {
private T foo;

public void setFoo(T foo) {
this.foo = foo;
}

public T getFoo() {
return foo;
}
}

<T> 用来声明一个类型持有者(Holder)T,之后您可以用 T 作为类型代表来声明变量(引用)名称,然后您可以像下面的程序来使用这个类:
GenericFoo<Boolean> foo1 = new GenericFoo<Boolean>();
GenericFoo<Integer> foo2 = new GenericFoo<Integer>();
 
foo1.setFoo(new Boolean(true));
Boolean b = foo1.getFoo();
 
foo2.setFoo(new Integer(10));
Integer i = foo2.getFoo();
 

不同的地方在于,在声明与配置对象时,您可以一并指定泛型类中真正的类型,这将用来取代定义时所使用的 T,而这次您可以看到,接口转换不再需要了,所定义出来的泛型类在使用时多了一层安全性,至少可以省去恼人的ClassCastException 发生,编译器可以帮您作第一层防线,例如下面的程序会被检查出错误:
GenericFoo<Boolean> foo1 = new GenericFoo<Boolean>();
 
foo1.setFoo(new Boolean(true));
Integer b = foo1.getFoo();
 

foo1.getFoo()传回的是Boolean,您要将它指定给Integer,这显然语法上不合,编译器这时可以派上用场:
Test.java:7: incompatible types
found : java.lang.Boolean
required: java.lang.Integer
Integer b = foo1.getFoo();

如果使用泛型类,但声明时不一并指定类型呢?那么默认会使用Object,不过您就要自己转换对象的接口类型了,但编译器会提出警讯,告诉您这可能是不安全的操作:
Note: Test.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

回过头来看看下面的声明:
GenericFoo<Boolean> foo1 = new GenericFoo<Boolean>();
GenericFoo<Integer> foo2 = new GenericFoo<Integer>();
 

GenericFoo< Boolean>声明的foo1与GenericFoo< Integer>声明的foo2是相同的类型吗?答案是否定的,基本上它们分属于两个不同类的类型,即“相当于”下面两个类型(只是个比喻):
public class GenericFooBoolean {
    private Boolean foo;
 
    public void setFoo(Boolean foo) {
        this.foo = foo;
    }
 
    public Boolean getFoo() {
        return foo;
    }
}
 
以及:
public class GenericFooInteger {
    private Integer foo;
 
    public void setFoo(Integer foo) {
        this.foo = foo;
    }
 
    public Integer getFoo() {
        return foo;
    }
}
 

所以您不可以将 foo1 指定给 foo2,或是将 foo2 指定给 foo1,编译器会回报以下错误:
incompatible types
found : GenericFoo<java.lang.Integer>
required: GenericFoo<java.lang.Boolean>
foo1 = foo2;