1.4 常用的泛型:使用泛型参数来编写方法
Java5+
前面的小节介绍了泛型可以简化Java代码并使代码能够防范ClassCastException错误。除了作为JDK的一部分来使用泛型之外,还可以编写你自己的泛型。当对类型相同的对象进行操作时泛型是很有用的,但是对象的具体类型直到对类实例化时才能知道。这种方式非常适合于包含关联项目的集合或涉及查找的类。
下面编写一个使用泛型参数的方法。回想一下前面是怎样使用ArrayList类的—— 只在构造ArrayList时才指定它使用哪些对象类型。注意,在定义类时并不知道其类型,并且不能将java.lang.Object作为类型使用,因为最后将遇到类似以前的类型强制转换问题。当定义泛型时,必须使用一种特殊的语言来代表类型。当声明类名时要完成此操作。在下面的示例中,<T>表示一种类将使用的类型:
public class RandomSelection<T> { }
|
这里,类型指示符的尖括号看起来类似HTML语法,但实际上和HTML没有关系,它们也不表示小于或大于!尖括号在一个泛型的类名与一个类型相结合的情况下使用,正如前面的ArrayList<Integer>那样。尽管直到调用构造函数时才知道真实的类型,但我们可以在方法定义中使用替换类型。假定定义了一个叫做RandomSelection的类,该类使用另一个类的某个类型,暂时将该类型称为T。但是,此类的名字仍是RandomSelection。另外,每次可以对多个类型执行这种操作,正如java.util.Map的定义所示的那样。在这种情况下,在类名之后使用一个由逗号分隔的标识符列表即可。
public class MyGeneric<T,U,V> { }
|
上面定义的MyGeneric类涉及到三个类型,它们分别称为T、U和V。下面编写一个方法来扩展RandomSelection类,该方法将一个项目添加到一个由内部管理的泛型(类型为T)的ArrayList中:
public class RandomSelection<T> {
private ArrayList<T> list;
public RandomSelection() {
list = new ArrayList<T>();
}
public void add(T element) {
list.add(element);
}
}
|
注意,实际上并不是处理一个叫作T的类。T代表当某人创建RandomSelection的一个实例时使用的任意类。Java规范允许使用任意标识符,但是一般是使用单个大写字母来和普通的类名进行区别。既然已经定义add方法接受一个类型T参数,则只能使用在构造RandomSelection实例时采用的相同的类型来调用此方法。以下的代码是非法的并会产生一个编译错误:
RandomSelection<String> rs = new RandomSelection<String>();
rs.add(new Date()); // illegal for a RandomSelection<String>
|
如果希望一个方法返回一个泛型类型,可以将方法签名的返回类型设为T,正如下面的定义所示:
import java.util.Random;
public class RandomSelection<T> {
private java.util.Random random = new Random();
// ..... earlier methods omitted
public T getRandomElement() {
int index = random.nextInt(list.size());
return list.get(index);
}
}
|
getRandomElement方法返回一个类型T,即与在类声明中定义的类型相同。通过构造一个类型实例,现在可以使用刚才定义的RandomSelection类:
RandomSelection<Integer> selector = new RandomSelection<Integer>();
selector.add(2);
selector.add(3);
selector.add(5);
selector.add(7);
selector.add(11);
Integer choice = selector.getRandomElement();
System.out.println(choice);
|
给一个整型变量choice赋值是安全的,因为selector的getRandomElement方法返回的总是一个Integer。情况确实如此,因为是使用Integer作为泛型类型来构造的selector实例。Add和getRandomElement方法的定义具有和构造函数的定义相同的类型,并且编译器将会强制执行此约束。尝试在构造函数中以一个不同的类型来使用RandomSelection类,这次使用前面定义的Fruit enum类:
RandomSelection<Fruit> fruitSelector = new RandomSelection<Fruit>();
fruitSelector.add(Fruit.APPLE);
fruitSelector.add(Fruit.ORANGE);
fruitSelector.add(Fruit.GRAPEFRUIT);
fruitSelector.add(Fruit.BANANA);
fruitSelector.add(Fruit.DURIAN);
Fruit fruitChoice = fruitSelector.getRandomElement();
System.out.println(fruitChoice);
|
可以看出,能够直接使用来自getRandomElement方法的Fruit返回值,正如前面对待Integer那样。如果你想要一个类与某种类型(直到构造该类时才知道具体的类型)的对象协同操作以及希望编译器严格执行类型限制,那么你可以定义自己的泛型。这样做的主要优点体现在它的安全和便利性上。若想了解有关泛型的更多信息,请查阅网址http:// java.sun.com/j2se/1.5.0/docs/guide/language/generics.html上的Generics Tutorial(泛型指南) 和Java 5文档。