1、什么是泛型
泛型,是“參數(shù)化類型”的概念,代碼可以適用于多種類型。參數(shù)化類型是指把具體的類型參數(shù)化,就像方法中使用的參數(shù)一樣。
void hello(String str){
System.out.println("str是個(gè)變量參數(shù),String是個(gè)具體類型");
}
<T> void hello(T str){
System.out.println("str是個(gè)變量參數(shù),T是個(gè)類型參數(shù)");
}
2、為什么使用泛型
泛型出現(xiàn)最引人注目的一個(gè)原因,是為了創(chuàng)造使用容器類。
List arrayList=new ArrayList();
arrayList.add("hello");
arrayList.add(110);
for(Object obj:arrayList){
String s=(String)obj;//java.lang.ClassCastException
}
有些情況下,我們希望容器能夠持有多種類型的對(duì)象,但是,大部分情況我們只會(huì)使用容器存儲(chǔ)一種類型的對(duì)象,泛型可以在編譯階段就可以防止錯(cuò)誤的發(fā)生。
List<String> arrayList=new ArrayList<String>();
arrayList.add(110);//編譯時(shí)報(bào)錯(cuò)
另外,一般的類和方法,只能使用具體的類型。如果要編寫可以應(yīng)用于多種類型的代碼,這種刻板的限制就會(huì)對(duì)代碼造成束縛。
return語(yǔ)句只能返回一個(gè)對(duì)象,但是有很多功能要求一個(gè)方法需要返回多個(gè)對(duì)象,怎么辦?
解決方法就是創(chuàng)建一個(gè)對(duì)象,用它來(lái)持有返回的多個(gè)對(duì)象??梢栽诿看问剐枰獣r(shí),專門創(chuàng)建一個(gè)類,而使用泛型則能夠一次性解決該問(wèn)題,同時(shí)在編譯期間就能確保類型安全。
public class TwoTuple <A,B>{
public final A a;
public final B b;
public TwoTuple(A a,B b) {
this.a=a;
this.b=b;
}
}
例:獲取一個(gè)學(xué)生的信息和導(dǎo)師的姓名。
public class TupleTest{
public static void main(String[] args) {
TwoTuple<Student,String> tt=new TwoTuple<Student, String>(new Student(), "張大俠");
}
}
class Student{}
3、泛型特性
泛型只在編譯階段有效
List<String> stringArrayList = new ArrayList<String>();
List<Integer> integerArrayList = new ArrayList<Integer>();
Class<? extends List> classStringArrayList = stringArrayList.getClass();
Class<? extends List> classIntegerArrayList = integerArrayList.getClass();
if(classStringArrayList.equals(classIntegerArrayList)){
System.out.println("類型相同");
}
輸出:
類型相同
泛型不強(qiáng)制使用,泛型也無(wú)法顯示用于運(yùn)行時(shí)類型的操作,例如轉(zhuǎn)型、instanceof和new表達(dá)式。
public void testClass(){
GenricClass<Integer> gci=new GenricClass<Integer>(10086);
// GenricClass<String> gcs=new GenricClass<String>(10086);
GenricClass<String> gcs=new GenricClass<String>("hello");
GenricClass g1=new GenricClass(10086);
GenricClass g2=new GenricClass("hello");
GenricClass g3=new GenricClass(66.666);
GenricClass g4=new GenricClass(true);
System.out.println(g1.getField());
System.out.println(g2.getField());
System.out.println(g3.getField());
System.out.println(g4.getField());
if(g2 instanceof GenricClass<?>){//if(g2 instanceof GenricClass<String>) 編譯錯(cuò)誤
System.out.println("true");
}
}
Java泛型是使用擦除來(lái)實(shí)現(xiàn)的,意思是當(dāng)你使用泛型的時(shí)候,任何具體的類型信息都被擦除了,你唯一知道的就是你在使用一個(gè)對(duì)象。List<String> 和List<Integer>在運(yùn)行時(shí)是相同的類型,都被擦除成他們的原生類型,即List。
擦除默認(rèn)會(huì)把類型擦除到Object,如果需要使用具體類的某些特性,需要協(xié)助泛型類,給泛型類定個(gè)邊界。
class Shape{int length() {return 0;}}
class Rectangle extends Shape{}
class Circle extends Shape{}
class Painter<T extends Shape>{
private T t;
public Painter(T t) {this.t=t;}
public void draw(){
t.length();
}
}
在上面這個(gè)例子中,泛型并沒(méi)有貢獻(xiàn)什么好處,只要把T替換成Shape就可以很容易創(chuàng)建出沒(méi)有泛型的類,這也說(shuō)明一點(diǎn):只有當(dāng)你是用的類型參數(shù)比某個(gè)具體類型更加通用時(shí),泛型才有幫助。
4、怎么用泛型
泛型有三種使用方式:泛型類、泛型接口、泛型方法。
public interface GenricInterface<T> {
public T getField();
}
public class GenricClass<T> implements GenricInterface<T>{
private T field;
public GenricClass(T f) {field=f;}
public T getField() {return field;}
// 靜態(tài)泛型方法無(wú)法訪問(wèn)類中定義的類型。必須額外聲明
public static <T> T genricMethod(Class<T> tc) throws InstantiationException, IllegalAccessException{
T t=tc.newInstance();
return t;
}
public void show1(T t){System.out.println(t.toString());}
public <E> void show2(E e){System.out.println(e.toString());}
public <T> void show3(T t){System.out.println(t.toString());}
}
前面說(shuō)過(guò)了泛型無(wú)法通過(guò)new T( )創(chuàng)建對(duì)象實(shí)例,Java的解決方案是使用工廠對(duì)象來(lái)創(chuàng)建新的實(shí)例,最便利的工廠對(duì)象就是Class對(duì)象,可以通過(guò)newInstance()來(lái)創(chuàng)建該類型新對(duì)象。
public void testGenricMethod(){
System.out.println("泛型方法測(cè)試----------------------------------------------------------");
try {
NormalClass nc= GenricClass.genricMethod(NormalClass.class);
} catch (InstantiationException | IllegalAccessException e ) {
e.printStackTrace();
}
GenricClass<Integer> g1=new GenricClass<Integer>(10086);
g1.show1(100);
// g1.show1(100.1);
g1.show2(100); g1.show2(100.1);
g1.show3(100); g1.show3(100.1);
}
泛型通配符
public void testWildcard(){//通配符測(cè)試
System.out.println("通配符測(cè)試----------------------------------------------------------");
GenricClass<Integer> g1=new GenricClass<Integer>(10086);
GenricClass<Number> g3=new GenricClass<Number>(66.666);
// show(g1);//參數(shù)不匹配
show(g3);
show2(g1); show2(g3);
show3(g1); show3(g3);
// show4(g1); show4(g3);//參數(shù)不匹配 <?>可理解為所有泛型父類<? extends Object>
}
public void show(GenricClass<Number> gc){
System.out.println("show:"+gc.getField());
}
public void show2(GenricClass<? extends Number> gc){
System.out.println("show2:"+gc.getField());
}
public void show3(GenricClass<?> gc){
System.out.println("show3:"+gc.getField());
}
public void show4(GenricClass<Object> gc){
System.out.println("show4:"+gc.getField());
}