泛型简介
Java SE 5.0中增加泛型机制的主要原因是为了满足在1999年定制的最早的Java规范需求之一(JSR 14)。专家组花费了五年左右的时间来定义规范和测试实现。使用范型程序编写的程序代码比起那些杂乱的使用Object变量,然后再进行强制类型转化的代码具有更好的安全性和可读性,范型对于集合尤其有用。
为什么要使用泛型程序设计
泛型程序设计(Generic programming)意味着编写的代码可以被很多不同类型的对象所重用。
在Java SE 5.0之前,Java泛型程序设计上用继承实现的。ArrayList类只维护一个Object引用的数组:
public class ArrayList // before Java SE 5.0 { public Object get(int i) { ... } public void add(Object o) { ... } ... private Object[] add(Object o); }
这样的实现有两个问题。第一个问题是当获取一个值时必须进行强制类型转换,第二个问题是这里没有错误检查,因此可以向集合ArrayList中添加任何类的对象。随着Java SE 5.0推出的泛型,它提供了一个更好的解决方案:类型参数(type parameters)。ArrayList类有一个类型参数用来指示元素的类型,使得程序具有更好的可读性和安全性。
泛型程序设计划分为三个阶段,基本级别是:
1.仅仅使用泛型类 - 典型的说像ArrayList这样的集合,不必考虑它们的工作方式;
2.当把不同的范型类混合在一起时,可能会看到含混不清的错误消息,这样的话就需要学习Java泛型来系统的解决这些问题;
3.最后可能想要实现自己的泛型类与泛型方法;
简单泛型类的定义
一个泛型类(generic class)就是具有一个或多个类型变量的类。举例来说就是:
package cn.net.bysoft; public class Pair<T> { public Pair() { this.first = null; this.sceond = null; } public Pair(T first, T sceond) { super(); this.first = first; this.sceond = sceond; } public T getFirst() { return first; } public void setFirst(T first) { this.first = first; } public T getSceond() { return sceond; } public void setSceond(T sceond) { this.sceond = sceond; } private T first; private T sceond; }
简单的泛型类
Pair类引入了一个变量T,用尖括号括起来,并放在类名的后面。泛型类可以有多个类型变量。例如Pair类中的第一个域和第二个域可以使用不同的类型:
public class Pair<T, U> { ... }
注意:类型变量使用大写形式,且比较短,这是很常见的,在Java库中,使用变量E表示集合的元素类型,K和V分别表示表的关键字与值的类型,T(需要时还可以用临近字母U和S)表示任意类型。换句话说,泛型类可以看作是普通类的工厂,具体的使用方法如下:
package cn.net.bysoft; // 找出数组中最小与最大的元素。 public class ArrayAlg { public static Pair<String> minmax(String[] a) { if (a == null || a.length == 0) return null; String min = a[0]; String max = a[0]; for (int i = 0; i < a.length; i++) { if(min.compareTo(a[i]) > 0) min = a[i]; if(max.compareTo(a[i]) < 0) max = a[i]; } return new Pair<String>(min, max); } } package cn.net.bysoft; public class PairTest { public static void main(String[] args) { // TODO Auto-generated method stub String[] words = { "Mary", "had", "a", "little", "lamb" }; Pair<String> mm = ArrayAlg.minmax(words); System.out.println(mm.getFirst()); System.out.println(mm.getSceond()); } }
简单的泛型类的使用例子
泛型方法
前面介绍了如何定义一个泛型类,实际上,还可以定义一个带有类型参数的简单方法。
package cn.net.bysoft; public class ArrayAlg { public static <T> T getMiddle(T[] a) { return a[a.length / 2]; } }
泛型方法可以在普通类中定义,调用时,在方法名前的尖括号中放入具体的类型:
String[] words = { "Mary", "had", "a", "little", "lamb" }; String word = ArrayAlg.<String>getMiddle(words);
大多数情况下,方法调用中可以省略<String>类型参数。编译器有足够的信息能推断出所调用的方法。它用names的类型(即String[])与泛型T[]进行匹配并拖断处T一定是String。也就是说,可以调用:
String word = ArrayAlg.getMiddle(words);