本文为个人摘抄《Java 核心技术 卷I(第十版)》
Java 中的泛型
为什么要使用泛型程序设计?
泛型程序设计(Generic programming)意味着编写的代码可以被很多不同类型的对象重用
在没有泛型的时候,泛型设计是怎么实现的?答案是使用继承
public class ArrayList //before generic class
{
private Object[] elementData;
...
public Object get(int i){...}
public void add(Object o){...}
}
对于使用这种方法,会出现两个问题:
1. 如果要获取一个值,必须进行强制类型转换(因为返回值是Object型)
2. 没有错误检查机制,可以添加任何类的对象(所有类都是Object型)
泛型提供了更好的解决方案:类型参数,如ArrayList就用类型参数来指示元素类型:
ArrayList<<String>> files=new ArrayList<String>();
使用类型参数的好处是
1. 使得代码具有更好的可读性
2. 使得编译器知道调用相应方法的时候的返回值类型,不需要用Object进行强制类型转换。
3. 编译器可以进行检查,避免插入错误类型的对象。
定义简单泛型类
一个泛型类就是具有一个或多个类型变量的类
public class Pair<T> //引入 类型变量T ,并用尖括号扩起,可以有多个泛型,用逗号隔开 如 <T,U>
{
private T first; //成员变量为类定义中的变量类型
private T second;
public Pair() { first = null; second = null; }
public Pair(T first, T second) { this.first = first; this.second = second; }
public T getFirst() { return first; } //方法返回类型为类定义中的变量类型
public T getSecond() { return second; }
public void setFirst(T newValue) { first = newValue; }//方法参数类型为类定义中的变量类型
public void setSecond(T newValue) { second = newValue; }
}
注意:一般来说,类型变量使用大写形式,而且比较短
在Java 库中,使用变量E表示集合的元素类型,K,V表示关键值对、T、U、S表示任意类型
初始化时,用具体的类型变量替换类型变量的位置即可实例化
Pair<String>
除了泛型类以外,我们还可以在类中定义泛型方法
Class ArrayAlg
{
Public staitc <T> T getMiddle(T…a) //类型变量放在修饰符后面 返回类型前
{
Return a[a.length/2];
}
}
调用时在尖括号中放入具体类型
String middle=ArrayAlg.getMiddle("John","Q","Public");
几乎大多数泛型调用是没有问题的,但是还是会有情况会出错
Double middle =ArrayAlg.getMiddle(3.14,1729,0)
在这里编译器会自动打包参数为一个Double和2个Interger对象,然后寻找这些类的共同超类,而后找到Number和Comparable接口,但是本身他们也是泛型类型。
如果想要不出错,只能把参数全部改成double \让参数具有共同超类
类型变量 的限定
看到上面的例子,有时我们需要对类型变量进行一点小小的约束。比如下面计算数组最小元素的例子
public static T min(T[] a)
变量类型为泛型,意味着可以是任何一个类的对象,怎么样才能限定进入方法的变量具有compareTo方法呢?
那么,可以按照这样的方法对类型变量设置限定(bound)实现:
class ArrayAlg
{
public static <T extend Comparable> T min(T[] a)
{
if(a==null|| a.length==0)
{
return null;
}
T smallest=a[0];
for(int i=0;i<a.length;i++)
{
if(smallest.compareTo(a[i])>0)
{
smallest=a[i];
}
}
return smallest;
}
}
这样如果没有实现Comparable的类调用就会产生编译错误
一个类型变量或者通配符可以有多个限定
例如
T extends Comparable &Serializable //限定类型用 "&" 分隔
在继承中,一个类只能继承一个类,但是可以实现多个接口,所以在泛型的限定中,至多有一个类,如果使用一个类作为限定,那么它必须是限定列表中的第一个。