1.简介
泛型是JDK1.5后增加的最重要的Java语言特性,可以在编译阶段约束操作的数据类型,并进行检查。如果不写泛型,集合类型默认存储的类型为Object类型。注意:<>中只能加引用数据类型;java中的泛型是伪泛型。
使用泛型可以针对不同的类有相同的处理方法(在一定意义上也是一种方便使用的多态的用法),例:
Vector <String> v = new Vector <String>();
v.addElement(“one”);
String s = v.elementAt(0);
同时,泛型的使用让类型更加安全(避免强制类型的转换,强转容易出现类型转换异常);类的适用也更加广泛,针对不同的类有相同的处理方法,但这些类之间不一定有继承关系。
2.泛型的定义
在实际操作中,我们希望编写的代码可以被很多不同类型的对象使用。这需要我们参数化类型。同时避免类型转换,这样,在实例化对象中,代码可以在一种相同框架下,专门针对类型的不同实例化对象,使得代码可以重复使用。
下面分不同的情况进行讨论:
2.1泛型类
泛型类就是具有一个或多个类型变量的类。
使用场景:当一个类中,某个变量的数据类型不确定时,就可以定义带有泛型的类。
语法:class className<T>{}
下面是一个泛型类的例子:
import java.util.*;
public class TNode<T> {
private T value;
private ArrayList<TNode<T>> children=new ArrayList<>();
TNode(T v){
this.value=v;
}
public T getValue() {
return this.value;
}
public void add(T v) {//添加子节点
TNode<T> child=new TNode<>(v);
this.children.add(child);
}
public TNode<T> getChild(int i){
if((i<0)||(i>this.children.size())) return null;
return (TNode<T>) this.children.get(i);
}
public void traverse() {//遍历
System.out.println(this.value);
for(TNode child:this.children)
child.traverse();
}
}
TNode类中引入一个类型变量T,我们把它用尖括号(<>)将其扩起来,放在类名的后面。当然,泛型类可以有多个类型变量。例如:可以这样TNode类,其中第一字段和第二字段使用不同的类型:
public class TNode<T,U>{……}
类型变量是指在整个类定义中用于指定方法返回类型以及字段和局部变量的类型。
例如:
private T value;中的T;
这样在对象的实例化的过程中可以做到流水线化,按需求对对类实例化为对象。例如:
TNode<String> t=new TNode<>();
TNode<Integer> t=new TNode<>();
以下是TNode实例化的具体实例(GenerTreeClass.java):
public class GenerTreeClass {
public static void main(String[]args) {
TNode<String> t=new TNode<>("Roo");
t.add("left");
t.add("Middle");
t.add("Right");
t.getChild(0).add("aaa");
t.getChild(0).add("bbb");
t.traverse();
}
}
运行结果如下:
相关例子:实现一个简单的ArrayList类
相关代码如下:
import java.util.Arrays;
public class MyArrayList <E>{
private int size;
private Object[] obj=new Object[size];
public boolean add(E e){
obj[size]=e;
size++;
return true;
}
public E get(int index){
return (E)obj[index];
}
public String toString(){
return Arrays.toString(obj);
}
}
2.2泛型方法
应用场景:方法中形参类型不确定。
方案一:使用类名后面定义的泛型。---》所有方法都能用
方案二:在方法声明上定义自己的泛型。---》只有本方法能用
泛型方法,及带有类型参数的方法。例:
public class ArrayAlg{
public static <T> T getMiddle(T….a){
return a[a.length/2];
}
}
我们可以看到,这个方法是在普通类中定义的,而不是在泛型类中。例子中,getMiddle(T…a)是一个泛型方法,从尖括号和类型变量可以看出。Attention:在泛型方法定义时,类型变量放在修饰符(例子中的public static)后面,并在返回类型的前面。
泛型方法可以在普通类中定义,也可以在泛型类中定义。在进行调用时,可以把具体类型包围在尖括号中放在方法名前面,例:
String middle=ArrayAlg .<String>getMiddle(“ASD”,”ABC”,”123”);
泛型方法的完整示例如下:
import java.util.ArrayList;
public class ListUtil {
public static <E> void addAll(ArrayList<E> list,E ... e){
for(E element : e){
list.add(element);
}
}
}
2.3泛型接口
当我们需要泛化子类方法时,需要用到泛型接口。泛型接口的定义与泛型类相似,在泛型类名后面加<T>,T用来指定方法返回值和参数,实现接口时,应当指定类型。(同时,泛型接口本身可以在生成一个同名泛型类。)下面是一个关于泛型接口的例子:
public interface Calcular<T>{
Public T add(T operand1,T operand2);
}
public class IntegerCalcular implements Calcular<Integer>{
Public Integer add(Integer operand1,Integer operand2){
Return operand1+operand2;
}
}
2.4泛型类型限定
在特定场合下,我们需要对类型进行限定(使用某些特定方法),比如说下面这段代码:
public static <T extends Comparable> T getMin(T…a){
if(null==a||a.length<=0){
Return null;
}
T min=a[0];
for(int i=1;i<a.length;i++){
if(min.compareTo(a[i])>0){
min=a[i];
}
}
return min;
}
注意:T…表示一个不定项参数,其本质是一个T类型的数组。Java泛型限定是java泛型的高级功能,他的格式是:
<T extends Comparable>
这样约定T必须是Comparable的子类,这样可以对泛型类型进行限定。不管是类还是接口,extends统一固定,后面可以多个,用&拼接,例:
<T extends Comparable & Serializable>
extends限定可以有多个接口,但只能是一个类,且类必须排在第一位,用逗号隔参数,例:
<T extends File &Cloneable, U extends Serializable>
2.4.1泛型类之间的继承
在Java核心技术 卷1 上有这样一个实例:
Pair<Apple> apples=new Pair<>(new Apple(3),new Apple(4));
Pair<Fruit> fruits=apples;
fruits.setFirst(new Orange(5));//Apple,Orange继承于Fruit类。
注意:这里的Pair<S>和Pair<T>无任何关系,无论S和T之间是什么关系。泛型类可以拓展或实现其它类,例ArrayList<T>实现List<T>:
List<Orange> oranges=new ArrayList<Orange>();
2.4.2泛型通配符与类型限定
一.运用进行上限约束
我们先从一个例子说起:
public void printFruits(Pair<? extends Fruit> fruits){
Fruit first=fruits.getFirst();
Fruit second=fruits.getSecond();
System.out.println(“The size of fruits are”+first.getSize()+”, ”+
Second.getSize());
}
上面的例之中,我们利用通配符“?”来对Pair类进行限定,对Pair能接受的参数类型进行了限定,Pair<? extends S>表示Pair能接受的参数类型,是S自身或其子类,同时如果是Pair<? supet S> 表示Pair能接受的参数类型为S自身或其父类。