1、描述:泛型是類(lèi)和接口的一種擴(kuò)展機(jī)制,利用這種機(jī)制程序員可定義類(lèi)或接口的集合。由泛型定義實(shí)際的類(lèi)或接口時(shí),只需為泛型的每個(gè)類(lèi)型參數(shù)提供類(lèi)型實(shí)參。
2、簡(jiǎn)單的泛型類(lèi)聲明,形如:pubic class ClassName<K,V>(){類(lèi)體}
例子:
import java.util.Vector;
public class Test


{
public static void main(String args[])

{
Stack<String> strStack;
strStack=new Stack<String>(10);
strStack.push(" Generics Class.");
strStack.push("am");
strStack.push("I ");
System.out.println(strStack.pop()+strStack.pop()+strStack.pop());
}
}
class Stack<T>


{
private int stackSize=100;//設(shè)置棧的大小
Vector<T> stack=new Vector<T>();
public Stack(int size)

{
stack.setSize(size);
}
//入棧
public boolean push(T o)

{
if(stack.size()>=stackSize)

{
System.out.println("棧溢出");
return false;
}
else

{
return stack.add(o);
}
}
//出棧
public T pop()

{
if(!isEmpty())

{
T o=stack.lastElement();
stack.remove(o);
return o;
}
else

{
System.out.println("棧空");
return null;
}
}
//判定棧是否為空
public boolean isEmpty()

{
return stack.isEmpty();
}
}
3、受限泛型類(lèi)聲明:所謂受限是指類(lèi)型參數(shù)受限,其類(lèi)型參數(shù)列表一般格式如:
<T [extends] 類(lèi)t1或接口t1[& 接口t2 [& 接口 t3]……]],
E [extends] 類(lèi)e1或接口e1[& 接口e2 [& 接口 e3]……]],
……
>
這樣使其實(shí)參類(lèi)型限制于某個(gè)類(lèi)的派生類(lèi)或接口實(shí)現(xiàn)類(lèi),或一組接口的實(shí)現(xiàn)類(lèi)。
例子:
import java.util.Vector;
public class Test


{
public static void main(String args[])

{
//String不是有界參數(shù)的有效替代項(xiàng)
//Stack<String> strStack;
Stack<Academician> acaStack=new Stack<Academician>(10);
acaStack.push(new Academician("張三",20,"西方文學(xué)"));
acaStack.push(new Academician("李四",21,"工商管理"));
acaStack.push(new Academician("王五",19,"計(jì)算機(jī)科學(xué)與技術(shù)"));
System.out.println(acaStack.pop());
System.out.println(acaStack.pop());
System.out.println(acaStack.pop());
}
}
//這里的Student稱(chēng)為類(lèi)型參數(shù)的最左限制
//Stack<T>的最左限制為Object,因?yàn)槿魏晤?lèi)都是Object派生的
class Stack<T extends Student>


{
private int stackSize=100;//設(shè)置棧的大小
Vector<T> stack=new Vector<T>();
public Stack(int size)

{
stack.setSize(size);
}
//入棧
public boolean push(T o)

{
if(stack.size()>=stackSize)

{
System.out.println("棧溢出");
return false;
}
else

{
return stack.add(o);
}
}
//出棧
public T pop()

{
if(!isEmpty())

{
T o=stack.lastElement();
stack.remove(o);
return o;
}
else

{
System.out.println("棧空");
return null;
}
}
//判定棧是否為空
public boolean isEmpty()

{
return stack.isEmpty();
}
}
class Student


{
private String name;
private int age;
Student(String name,int age)

{
this.name=name;
this.age=age;
}
public String toString()

{
return "name:"+name+"\tage:"+age;
}
}
class Academician extends Student


{
private String specialty;
Academician(String name,int age,String specialty)

{
super(name,age);
this.specialty=specialty;
}
public String toString()

{
return super.toString()+"\tspecialty:"+specialty+"\n";
}
}
泛型類(lèi)只是類(lèi)的一種的擴(kuò)展,其實(shí)體的類(lèi)體與普通類(lèi)基本一樣,只是泛型類(lèi)的實(shí)例變量的類(lèi)型和實(shí)例方法的參數(shù)類(lèi)型及返回值可以參數(shù)化而已。如例中:void push(T o)、T pop()。但由于泛型的類(lèi)型參數(shù)在創(chuàng)建泛型類(lèi)對(duì)象時(shí)才傳入,是一個(gè)實(shí)例化參數(shù),所以不能使用在泛型類(lèi)的靜態(tài)成員域中,也不能用在靜態(tài)初始化塊中。
泛型類(lèi)可以看做類(lèi)的模板,程序員可以通過(guò)將具體的類(lèi)名作為泛型類(lèi)型參數(shù)的實(shí)參,傳入泛型中定義新的類(lèi)。如例中:Stack<Academician> acaStack=new Stack<Academician>(10);編譯器將用所提供的類(lèi)名Academician替換類(lèi)型參數(shù)T,生成一個(gè)想像類(lèi)Stack<Academician>,此類(lèi)實(shí)際上并不存在,只是java編譯器編譯時(shí)當(dāng)其存在。泛型的實(shí)參也可以使用泛型定義的類(lèi)型,如:Stack<Stack<String>> strStack=new Stack<Stack<String>>;
作為泛型類(lèi)型參數(shù)的實(shí)參,必須是引用數(shù)據(jù)類(lèi)型,不能是基本數(shù)據(jù)類(lèi)型。如果希望基本數(shù)據(jù)類(lèi)型的值存儲(chǔ)在堆棧中,可以使用其裝類(lèi):Integer、Short等。
用具體的類(lèi)型名稱(chēng)作為泛型類(lèi)型參數(shù)的實(shí)參,創(chuàng)建新類(lèi)型的過(guò)程稱(chēng)為類(lèi)型擦除。在這個(gè)過(guò)程中,所有的T將用最左限制類(lèi)型替換掉,像Stack(T)擦除后,用其最左限制替換它。省略了泛型類(lèi)型參數(shù)的類(lèi)型被稱(chēng)為泛型的原生類(lèi)型。
假如使用泛型Stack創(chuàng)建了如下兩個(gè)不同的類(lèi)型:
Stack<Integer> I=new Stack<Integer>(10);
Stack<Integer> D=new Stack<Integer>(10);
顯然,編譯器會(huì)將其作為不同的類(lèi)型(因此I和D之間不能進(jìn)行類(lèi)型轉(zhuǎn)換),但在運(yùn)行時(shí)共享相同的類(lèi)名——可以使用instanceof進(jìn)行測(cè)試。也就是說(shuō)泛型生成的類(lèi)具有相同的運(yùn)行時(shí)類(lèi)名稱(chēng),也正因?yàn)槿绱耍盒蜕深?lèi)型的實(shí)例無(wú)法用instanceof關(guān)鍵字確定其類(lèi)型。
4、通配符
為了表示泛型定義的類(lèi)型集合中的某個(gè)具體類(lèi)型,需要為泛型的每個(gè)類(lèi)型參數(shù)提供類(lèi)型實(shí)參,否則編譯器將會(huì)提示警告信息。為此要判斷泛型Stack<T>產(chǎn)生的新類(lèi)型:Stack<Integer>、Stack<Double>、Stack<String>是否為空,需要定義一組生載方法,很麻煩。為此,java提供了泛型參數(shù)通配符<?>,通配符可以代表任何類(lèi)或接口(基本類(lèi)型不包括在內(nèi))。看一個(gè)例子:
import java.util.Vector;
public class Test


{
public static boolean isEmpty(Stack<?> s)

{
return s.isEmpty();
}
public static void main(String args[])

{
Stack<Integer> intStack=new Stack<Integer>(10);
Stack<Double> dblStack=new Stack<Double>(10);
Stack<String> strStack=new Stack<String>(10);
if(!isEmpty(intStack))

{
System.out.println("intStack是空棧!");
}
if(!isEmpty(dblStack)&&!isEmpty(strStack))

{
System.out.println("dblStack和strStack都是空棧!");
}
}
}
class Stack<T>


{
private int stackSize=100;//設(shè)置棧的大小
Vector<T> stack=new Vector<T>();
public Stack(int size)

{
stack.setSize(size);
}
//入棧
public boolean push(T o)

{
if(stack.size()>=stackSize)

{
System.out.println("棧溢出");
return false;
}
else

{
return stack.add(o);
}
}
//出棧
public T pop()

{
if(!isEmpty())

{
T o=stack.lastElement();
stack.remove(o);
return o;
}
else

{
System.out.println("棧空");
return null;
}
}
//判定棧是否為空
public boolean isEmpty()

{
return stack.isEmpty();
}
}
java提供了兩種方法,將通配符限制在某一個(gè)范圍內(nèi):
(1)通配符的上界(<? extends 類(lèi)或接口>):將通配符限制為指定的類(lèi)及其派生類(lèi)或接口的實(shí)現(xiàn)類(lèi)。
(2)通配符的下界(<? super 類(lèi)或接口>);將通配符限制為指定的類(lèi)或其父類(lèi)。
5、泛型類(lèi)的繼承問(wèn)題:
方式一:
public class IntStack extends Stack<Integer>


{
public IntStack(int size)

{
super(size);
}
public static void main(String args[])

{
IntStack intStack=new IntStack(10);
intStack.push(3);
System.out.println(intStack.pop());
}
}
方式二:SubStack(T)的T和Stack(T)的T相對(duì)應(yīng),保持一致。
public class SubStack<T> extends Stack<T>


{
public SubStack(int size)

{
super(size);
}
public static void main(String args[])

{
SubStack<Integer> intStack=new SubStack(10);
intStack.push(3);
System.out.println(intStack.pop());
SubStack<String> strStack=new SubStack(10);
strStack.push("Hello");
System.out.println(strStack.pop());
}
}
6、泛型接口
(1)定義:
public interface Stackable<T>


{
boolean push(T o);
T pop();
boolean isEmpty();
}
(2)實(shí)現(xiàn):
public class ImpStack implements Stackable<String>


{
public boolean push(String s)

{省略具體實(shí)現(xiàn)}
public String pop()

{省略具體實(shí)現(xiàn)}
public boolean isEmpty()

{省略具體實(shí)現(xiàn)}

}
或者
public class ImpStack<T> implements Stackable<T>


{
public boolean push(T o)

{省略具體實(shí)現(xiàn)}
public T pop()

{省略具體實(shí)現(xiàn)}
public boolean isEmpty()

{省略具體實(shí)現(xiàn)}
}
7、泛型數(shù)組:java編譯器不允許創(chuàng)建泛型生成類(lèi)的數(shù)組,但允許用無(wú)界限通配符作為實(shí)參得到的類(lèi)型定義數(shù)組,如:List<?> ls=new List<?> [2];這種類(lèi)型的數(shù)組,簡(jiǎn)稱(chēng)為通配符數(shù)組。雖然通配符數(shù)組存在著類(lèi)型安全問(wèn)題,如果使用恰當(dāng)還是非常靈活好用的。通配符數(shù)組的元素可以是任何泛型生成的具體類(lèi)型的對(duì)象或其派生類(lèi)的對(duì)象。例子:
import java.util.*;
class ArrayGeneric<T>


{
public static void listAll(List<?>[] ls)

{
for(List<?> obj:ls)

{
listAll(obj);
}
}
public static void listAll(List<?> ls)

{
for(Object obj:ls)

{
System.out.println(obj);
}
}
}
public class Test


{
public static void main(String args[])

{
ArrayList al=new ArrayList();
LinkedList lls=new LinkedList();
al.add("123");
al.add("156");
lls.add("abc");
lls.add("def");
List<?>[] ls=new List<?>[2];
ls[0]=al;
ls[1]=lls;
ArrayGeneric.listAll(ls);
}
}
注意:
List<?>[] words =new List<?> [20];
Object[] objs=words;
objs[0]=3;
這三行能通過(guò)編譯,但在運(yùn)行時(shí)會(huì)發(fā)生錯(cuò)誤
8、泛化方法:可對(duì)所有的方法進(jìn)行泛化,包括實(shí)例方法、靜態(tài)方法、構(gòu)造方法。
例子:
import java.util.*;
public class Test


{
public <T> Test(T t)

{
System.out.println(t);
}
public static <T> void print1(T t)

{
System.out.println(t);
}
public <T> void print2(T t)

{
System.out.println(t);
}
public static void main(String args[])

{
print1(2008);
print1("北京歡迎你!");
Test test1=new Test(2008);
Test test2=new Test("北京歡迎你!");
test1.print2(2008);
test1.print2("北京歡迎你!");
}
}