為什么使用泛型
泛型就是“參數(shù)化類型”完残,就是把類型當(dāng)作參數(shù)傳遞
對于為什么要使用泛型,我們先來舉幾個栗子
??1:
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) {
return a + b;
}
上面兩個方法的方法體實(shí)現(xiàn)都是一樣的均函,但是因?yàn)閰?shù)的類型和返回值的類型不同就需要定義兩個方法口予。
??2:
public class MainActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initViews();
initData();
}
}
public class TestActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initViews();
initData();
}
}
上面兩個activity類中實(shí)現(xiàn)的方法是一樣的规求,但是由于屬于不同的頁面導(dǎo)致我們要實(shí)現(xiàn)多個重復(fù)的代碼意荤。
??3:
List list = new ArrayList();
list.add(1);
list.add("a");
System.out.println((String)list.get(0) + (String)list.get(1));
報(bào)錯:
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
在編譯過程中辩尊,List中的類型應(yīng)該是Object
烟馅,強(qiáng)制類型轉(zhuǎn)換就有可能出錯说庭。
泛型的好處:
1、多種數(shù)據(jù)類型執(zhí)行相同的代碼
2郑趁、實(shí)例化時指定類型刊驴,不需要強(qiáng)制類型轉(zhuǎn)換
泛型類、泛型接口、泛型方法
泛型類:
public class T1<T>{}
泛型接口:
public interface T2<T>{}
泛型方法:
// 1. 普通泛型方法
// 1.1 方法調(diào)用時指定類型
public <T> T test1(T t){
return t;
}
// 1.2 類初始化時指定類型
public class T1<T>{
public T test1(T t){
return t;
}
}
// 2. 靜態(tài)泛型方法
public class T2<T>{
// 此處的T和T2后面的T沒有任何關(guān)系捆憎,
// T2中的T需要在初始化時指定類型舅柜,
// 此處的靜態(tài)方法是在方法調(diào)用時指定的
public static <T> T test(T t){
return t;
}
}
限定類型變量
有的時候,我們需要對類型變量(泛型)進(jìn)行約束攻礼,比如取兩個變量的最小值
public <T> T min(T a, T b) {
if (a.compareTo(b) > 0) return b;
else return a;
}
如何保證傳入的T
有compareTo
方法呢业踢?
public <T extends Comparable> T min(T a, T b) {
if (a.compareTo(b) > 0) return b;
else return a;
}
T extends Comparable & Serializable
泛型可以支持多個接口,如果限定類型有類礁扮,這個類必須是在限定列表的第一個知举。
泛型中的約束和局限性
-
不能實(shí)例化基本數(shù)據(jù)類型的泛型
// Test<double> t = new Test<double>();// 不允許這種寫法 Test<Double> t = new Test<Double>();
-
不能實(shí)例化類型變量
// T t = new T();
-
運(yùn)行時類型查詢,只能適用于原始類型
Test<Double> t = new Test<>(); //if(t instanceof Test<Double>) //不允許 //if(t instanceof Test<T>) //不允許 t.getClass();
-
泛型類靜態(tài)上下文中類型變量失效
class Test<T> { //此處的T和Test<T>不是同一個 public static <T> T getInstance(){ ... } }
-
不能創(chuàng)建參數(shù)化類型的數(shù)組
// Test<Double> ts = new Test<Double>[]; //不允許
-
不能捕獲泛型類型的實(shí)例
// class MyException<T> extends Exception //編譯器報(bào)錯 // class MyThrowable<T> extends Throwable //編譯器報(bào)錯 //public <T extends Throwable> void test(){ // try { // // }catch (T e){//此處T報(bào)錯 // // } //} public <T extends Throwable> void test(){ try { }catch (Throwable e){//可以這么寫 } }
通配符類型
-
? extends X
表示類型的上界太伊,類型參數(shù)是X的子類
-
? super X
表示類型的下界雇锡,類型參數(shù)是X的超類
無限定的通配符
Pair<?>。初看起來僚焦,這好像與原始的 Pair 類型一樣锰提。 實(shí)際上, 有很大的不同芳悲。類型 Pair<?> 有以下方法:
? getFi rst()
void setFirst(?)
getFirst 的返回值只能賦給一個 Object 立肘,setFirst 方法不能被調(diào)用, 甚至不能用 Object 調(diào)用名扛。Pair<?> 和 Pair 本質(zhì)的不同在于: 可以用任意 Object 對象調(diào)用原始 Pair 類的 setObject谅年;
為什么要使用這么脆弱的類型?
它對于許多簡單的操作非常有用肮韧。 例如融蹂, 下面這個方法 將用來測試一個 pair 是否包含一個 null 引用, 它不需要實(shí)際的類型弄企。
public static boolean hasNulls(Pair<?> p){
return p.getFirstO = null || p.getSecondO = null;
}
也可以使用:
public static <T> boolean hasNulls(Pair<T> p)
但帶有通配符可讀性更強(qiáng)一些超燃。
虛擬機(jī)中是如何實(shí)現(xiàn)泛型的?
在Java語言處于還沒有出現(xiàn)泛型的版本時拘领,只能通過Object是所有類型的父類和類型強(qiáng)制轉(zhuǎn)換兩個特點(diǎn)的配合來實(shí)現(xiàn)類型泛化意乓。由于Java語言里面所有的類型都繼承于java.lang.Object,所以O(shè)bject轉(zhuǎn)型成任何對象都是有可能的约素。但是也因?yàn)橛袩o限的可能性洽瞬,就只有程序員和運(yùn)行期的虛擬機(jī)才知道這個Object到底是個什么類型的對象。在編譯期間业汰,編譯器無法檢查這個Object的強(qiáng)制轉(zhuǎn)型是否成功伙窃,如果僅僅依賴程序員去保障這項(xiàng)操作的正確性,許多ClassCastException的風(fēng)險就會轉(zhuǎn)嫁到程序運(yùn)行期之中样漆。
C#中的泛型和Java中的泛型有什么區(qū)別为障?
C#在編譯成中間語言時List<int>和List<String>是兩種不同的類型,而在Java中,泛型只是一種語法糖鳍怨,編譯成class文件后泛型就會被擦除呻右,變成List<Object>。