1. 程式人生 > 實用技巧 >java中的泛型

java中的泛型

1.泛型是什麼?

泛型,即“引數化型別”。一提到引數,最熟悉的就是定義方法時有形參,然後呼叫此方法時傳遞實參。那麼引數化型別怎麼理解呢?顧名思義,就是將型別由原來的具體的型別引數化,類似於方法中的變數引數,此時型別也定義成引數形式(可以稱之為型別形參),然後在使用/呼叫時傳入具體的型別(型別實參)。

泛型的本質是為了引數化型別(在不建立新的型別的情況下,通過泛型指定的不同型別來控制形參具體限制的型別)。也就是說在泛型使用過程中,操作的資料型別被指定為一個引數,這種引數型別可以用在類、介面和方法中,分別被稱為泛型類、泛型介面、泛型方法。

2.泛型的特性

泛型只是在編譯階段有效,泛型資訊不會進入到執行階段。

例子:

public void test(){
/**
* 泛型只是在編譯階段有效,泛型資訊不會進入到執行階段
*/
List<String> stringArrayList = new ArrayList<String> ();
List<Integer> integerArrayList = new ArrayList<Integer>();
Class classString = stringArrayList.getClass();
Class classInt = integerArrayList.getClass();

System.out.println(classString);//class java.util.ArrayList

System.out.println(classInt); //class java.util.ArrayList
}
總結:泛型型別在邏輯上看以看成是多個不同的型別,實際上都是相同的基本型別。
3.泛型的型別
泛型根據使用方式,可分別為:泛型類、泛型介面、泛型方法三類。
3.1 泛型類
泛型型別用於類的定義中,稱為泛型類。通過泛型可以完成對一組類的操作對外開放相同的介面。最典型的就是各種容器類,如:List、Set、Map。
泛型類的寫法:
//此處T可以隨便寫為任意標識,常見的如T、E、K、V等形式的引數常用於表示泛型
//例項化泛型類的時候,必須指定T的具體型別
public class Generic<T>{
//key這個成員變數的型別為T,T的型別由外部指定
private T key;

public Generic(T key){ //泛型構造方法形參key的型別也為T,T的型別由外部指定
this.key = key;
}

public T getKey(){////泛型方法getKey的返回值型別為T,T的型別由外部指定
return key;
}

/**
* 這才是一個真正的泛型方法。
* 首先在public與返回值之間的<T>必不可少,這表明這是一個泛型方法,並且聲明瞭一個泛型T
* 這個T可以出現在泛型方法的任意位置,泛型的數量也可以有任意多個。
* 例如;public <T,K> K showKeyName(Generic<T> container){...}
*/
public <T> T showKeyName(Generic<T> container){
System.out.println("container key :" + container.getKey());
//當然這個例子舉的不太合適,只是為了說明泛型方法的特性。
T test = container.getKey();
return test;
}
}
3.2 類中的泛型方法
當然這並不是泛型方法的全部,泛型方法可以出現雜任何地方和任何場景中使用。但是有一種情況是非常特殊的,當泛型方法出現在泛型類中時,我們再通過一個例子看一下
public class Test1 {

//private int configId = 999;
class Fruit{
@Override
public String toString(){
return "fruit";
}
}

class Apple extends Fruit{
@Override
public String toString(){
return "apple";
}
}

class Person{
@Override
public String toString(){
return "Person";
}
}

class GenerateTest<T>{
public void show_1(T t){
System.out.println(t.toString());
}
//在泛型類中宣告一個泛型方法,使用泛型E,這種泛型可以為任意型別,可以型別與T相同,也可以不同
//由於泛型方法在宣告的時候會宣告泛型<E>,因此即使在泛型類中並未宣告泛型,編譯器也能夠正確識別泛型方法中識別的泛型
public <E> void show_3(E t){
System.out.println(t.toString());
}
//在泛型類中聲明瞭一個泛型方法,使用泛型T,注意這個T是一種全新的型別,可以與泛型類中宣告的T不是同一種類型。
public <T> void show_2(T t){
System.out.println(t.toString());
}
}

public void main(String[] args){
Apple apple = new Apple();
Person person = new Person();

GenerateTest<Fruit> generateTest = new GenerateTest<Fruit>();
//apple是Fruit的子類,所以可以
generateTest.show_1(apple);
//編譯器會報錯,因為泛型型別實參指定的是Fruit,而傳入的實參類是Person
//generateTest.show_1(person);

//show_2 show_3都是泛型方法,所以,對於apple和person都成功
generateTest.show_2(apple);
generateTest.show_2(person);

generateTest.show_3(apple);
generateTest.show_3(person);

}

}
3.3 泛型方法與可變引數
public <T> void printMsg(T... args){
for(T t : args){
System.out.print("泛型測試" + t);
}
}
printMsg("111",222,"aaaa","2323.4",55.55);
3.4 靜態方法與泛型
靜態方法有一種情況需要注意一下,那就是類中的靜態方法使用到泛型,靜態方法無法使用類上定義的泛型,如果靜態方法操作的引用資料型別不確定的時候,必須要將泛型定義在方法上。
即:如果靜態方法要使用泛型的話,必須將靜態方法也定義成泛型方法。
public class StaticGenerator<T> {
....
....
/**
* 如果在類中定義使用泛型的靜態方法,需要新增額外的泛型宣告(將這個方法定義成泛型方法)
* 即使靜態方法要使用泛型類中已經宣告過的泛型也不可以。
* 如:public static void show(T t){..},此時編譯器會提示錯誤資訊:
"StaticGenerator cannot be refrenced from static context"
*/
public static <T> void show(T t){

}
}
3.5 泛型方法總結
泛型方法能使方法獨立於類而產生變換,以下是一個基本原則:
無論何時,如果你能做到,你就該儘量使用泛型方法。也就是說,如果使用泛型方法將整個類泛型化,那麼就應該使用泛型方法。另外對於一個static的方法而已,無法訪問泛型型別的引數。
所以如果static方法要使用泛型能力,就必須使其成為泛型方法。
4.泛型的上下邊界
在使用泛型的時候,我們還可以為傳入的泛型型別實參進行上下邊界的限制,如型別實參只准傳入某種型別的父類或者某種型別的子類。
為泛型新增上邊界,即傳入的實參必須是指定必須是指定型別的子型別。

public void showKeyValue1(Generic<? extends Number> obj){
Log.d("泛型測試","key value is " + obj.getKey());
}
  Generic<String> generic1 = new Generic<String>("11111");
Generic<Integer> generic2 = new Generic<Integer>(2222);
Generic<Float> generic3 = new Generic<Float>(2.4f);
Generic<Double> generic4 = new Generic<Double>(2.56);

//這一行程式碼編譯器會提示錯誤,因為String型別並不是Number型別的子類
//showKeyValue1(generic1);
showKeyValue1(generic2);
showKeyValue1(generic3);
showKeyValue1(generic4);
//在泛型方法中新增上下邊界限制的時候,必須在許可權宣告與返回值之間的<T>上新增上下邊界,即在泛型宣告的時候新增 
//public <T> T showKeyName(Generic<T extends Number> container),編譯器會報錯:"Unexpected bound"
public <T extends Number> T showKeyName(Generic<T> container){
  System.out.println("container key :" + container.getKey());
   T test = container.getKey();
    return test;
}

通過以上例子可以看出泛型的上下邊界限制必須和泛型的宣告在一起。

轉載於: https://blog.csdn.net/s10461/article/details/53941091