泛型(generic)可以参数化类型。这个能力使我们可以定义带泛型类型的类或方法,随后编译器会用具体的类型来替换它。
Java定义了一个泛型类ArrayList用于存储泛型类型的元素。基于这个泛型类,可以创建出保存字符串或者数字的对象。这里的字符串和数字就是取代泛型类型的具体类型
泛型的动机和优点
从JDK 1.5开始,Java允许定义泛型类、泛型接口和泛型方法。
使用Java泛型的动机就是在编译时就会检测到错误,使程序更加可靠
Comparable c = new Date();
System.out.println(c.compareTO("red"));
在JDK 1.5之前这样编译时不会报错的,只有在运行时会产生错误。
Comparable<Date> c = new Data();
System.out.println(c.compareTo("red"));
JDK 1.5之后在编译时就会报错,泛型类型使程序更加可靠
例:
package java.lang
public interfact Comparable{
public int compareTo(Object o);
}
package java.lang
public interfact Comparable<T>{
public int compareTo(T o);
}
泛型类型必须是引用类型。不能使用int、double或char这样的基本类型来替换泛型类型
//这是错的
ArrayList<int> intList = new ArrayList<>();
//给int值创建一个ArrayList对象
ArrayList<Integer> intList = new ArrayList<>();
intList.add(15);
int i = intList.get(0);
Java会自动地将15包装为new Integer(15),这个过程称为自动打包(auto boxing),intList中的元素被赋给了int对象,intLIst(0)中的对象自动转化为基本类型的值。
定义泛型类
可以为类和接口定义泛型。当使用类来创建对象或者使用类或接口来声明引用变量时,必须指定具体类型
将一个存储对象的栈类修改,其元素类型通用化为泛型
GenericStack | |
---|---|
-list:java.util.ArrayList | 一个数组列表,用于存储元素 |
+GenericStack() | 创建一个空栈 |
+getsSze(): int | 返回栈中的元素数目 |
+peek(): E | 返回栈顶元素 |
+pop(): E | 返回并移除栈顶元素 |
+push(o: E): void | 添加一个新的元素到栈顶 |
+isEmpty(): boolean | 如果栈为空,返回true |
GenericStack类封装了栈的存储,并提供使用该栈的操作
public class GenericStack<E> {
private ArrayList<E> list = new ArrayList<>();
public int getSize(){
retrun list.size;
}
public E peek(){
retrun list.get(getSize()-1);
}
public E pop(){
E o = list.get(getSize()-1);
list.remove(getSize()-1);
retrun 0;
}
public E push(E o){
list.add(o);
}
public boolean isEmpty(){
retrun list.isEmpty();
}
@Override
public String toString(){
retrun "stack:" + list.toString();
}
}
创建栈时可以将在尖括号里面添加包装类型;可以不使用泛型,将元素类型设置成Object也可以容纳任何对象类型。但是使用泛型能够提高软件的可靠性和可读性,某些错误也可以在编译时被检测到
泛型方法
使用泛型类型来定义泛型方法。例如定义一个泛型方法print来打印一个对象数组
public static <E> void print(E[] list){
for(int i = 0;i<list.length;i++){
System.out.print(list[i] + " ");
System.out.println();
}
}
为了声明泛型方法,将泛型类型<E>方法写在方法头的关键字static之后
为了调用泛型方法,需要将实际类型放在尖括号中作为方法名的前缀或者简单调用。
可以将泛型指定为另外一种类型的子类型,这样的泛型类型称为受限的(bounded)。
public class BoundedTypeDemo{
public static void main(String[] args){
Rectangle rectangle = new Rectangle(2,2);
Cricle cricle = new Cricle(2);
//Rectangle和Cricle是GeometricObject的子类
System.out.println("Same area?" + equalArea(rectangle,cricle));
}
public static <E extends GeometricObject> boolean equalArea(E object1,E object2){
retrun object1.getArea() == object2.getArea();
}
}
equalArea方法用来测试两个集合对象是否具有相同的面积,受限的泛型类型<E extend GeometricObject>将E指定为GeometricObject的泛型子类型。必须传递两个GeometricObject的实例来使用equalArea
- 非受限泛型<E>相当于<E extends Object>
- 为一个类定义泛型类型,需要将泛型类型放在类名之后;为一个方法定义类型,要将泛型类型放在返回类型之前。
通配泛型
...{...
GenericStack<Integer> intStack = new GenericStack<>();
intStack.push(1);
intStack.push(2);
System.out.println(max(intStack));//会在这里编译错误
}
public static double max(GenericStack<Number> stack){
double max = stack.pop().doubleValue();
while(!stack.isEmpty()){
double value = stack.pop().doubleValue();
if(value>max)
max = value;
}
}
intStack 不是GenericStack < Number >的实例,因此不能调用max(),尽管Integer时Number的子类型,但是GenericStack不是GenericStack 的子类型,为了避免这个问题。可以使用统配泛型类型。统配类型有三种形式:?、? extends T、? super T,T是泛型类型。
- 第一种形式 ? 称为非受限统配,和? extends Object是一样的
- 第二种形式 ? extends T 称为受限统配,表示T或者T的子类型
- 第三种形式称为 ? super T称为下限统配,表示T或者T的一个父类型
那么将上面max方法修改以下:
public static double max(GenericStack<? extends Number> stack){...}
<? extends Number> 表示Number或者Number的子类型的通配类型
在方法中使用 ?通配符,表示任意一种对象类型。等价于<? extends Object>;如果用GenericStack替换为 GenericStack < ? >,调用方法会出错,Integer虽然是Object的子类型,凡是但是GenericStack< Integer >不是GenericStack< Object > 的子类型
<? super T>
...
//创建两个对象栈,假设stack1的类型为String,stack2的类型为Object
public static <T> void addd(GenericStack<T> stack1,GenericStack<? super T> stack2){
while(!stack1.isEmpty()){
stack.push(stack.pop());
}
}
若是用< T >代替<? super T>,在调用add方法是会产生一个编译错误,stack1的类型是GenericStack< String>,stack2的类型是GenericStack< Object>. <? super T>表示类型T或者是T的父类性。Object是String的父类性
也可以将add方法写成:
public static <T> void add(GenericStack<? extends T> stack1,GenericStack<T> stack2){...}