
1、泛型的基礎概念
1.1 為什么需要泛型
List list = new ArrayList();//默認型別是Object
list.add("A123");
list.add("B234");
list.add("C345");
System.out.println(list);
for(int i=0;i<list.size();i++){
//若要將list中的元素賦給String變數,需要進行型別轉換,不然會報Incompatible types錯誤,顯示list.get(i)回傳的是Object
String str = (String) list.get(i);
System.out.println(str);
}
?
list.add(123);//因為型別是Object,我們可以把Integer型別或者其他資料型別的元素也加入list之中
System.out.println(list.get(3));
?
for(int i=0;i<list.size();i++){
//String str = (String) list.get(i);//但是在這里會報錯java.lang.ClassCastException,我們不能直接將Integer型別的資料轉換成String
System.out.println(list.get(i).getClass());
}
如代碼中所示,當我們定義了一個List,list默認的型別是所有物件的基類Object,那么我們取出資料的時候需要經過一次型別轉換才能進行物件的實際型別的相關操作,因為List中的型別是Object,那么我們先添加了String型別的資料,然后再添加Integer或者其他型別的資料也是允許的,因為編譯時List中是Object型別的資料,然而運行的時候卻是它本身的型別,所以當我們將List中的資料當作String處理時會拋出java.lang.ClassCastException,
那么有沒有什么辦法可以使集合能夠記住集合內元素各型別,且能夠達到只要編譯時不出現問題,運行時就不會出現java.lang.ClassCastException例外呢?答案就是使用泛型,
1.2 什么是泛型
Java泛型設計原則是:只要在編譯時期沒有出現警告,那么運行時期就不會出現ClassCastException例外,
泛型,即“引數化型別”,把型別明確的作業推遲到創建物件或呼叫方法的時候才去明確的特殊型別,把<資料型別>當作是引數一樣傳遞,
相關術語:
-
ArrayList中的E稱為型別引數變數
-
ArrayList中的Integer稱為實際型別引數
-
整個稱為ArrayList泛型型別
-
整個ArrayList稱為引數化的型別
ParameterizedType
泛型的作用:
-
代碼更加簡潔【不用強制轉換】
-
程式更加健壯【只要編譯時期沒有警告,那么運行時就不會拋出
ClassCastException的例外】 -
可讀性和穩定性【在撰寫集合的時候,就限定了型別】
List<String> strlist = new ArrayList<String>();
List<Integer> intlist = new ArrayList<Integer>();
?
strlist.add("A");
strlist.add("B");
//strlist.add(123);//編譯時報錯
?
for(String str:strlist){
System.out.println(str);
//A
//B
}
?//加入Java開發交流君樣:756584822一起吹水聊天
System.out.println(strlist.getClass());//class java.util.ArrayList
System.out.println(intlist.getClass());//class java.util.ArrayList
System.out.println(strlist.getClass()==intlist.getClass());//true
泛型擦除
泛型是提供給javac編譯器使用的,它用于限定集合的輸入型別,讓編譯器在源代碼級別上,即擋住向集合中插入非法資料,但編譯器編譯完帶有泛型的java程式后生成的class檔案中將不再帶有泛型資訊,以此使程式的運行效率不受到影響,這個程序稱之為“擦除”,
泛型這個概念是JDK5提出的,JDK5以前的版本是沒有泛型的,需要建通JDK5以下的集合,當把帶有泛型特性的集合賦值給老版本的集合的時候,會把泛型給擦除了,它保留的是型別引數的上限,即Object,而當我們將沒有型別引數的集合賦給帶型別引數的集合,也不會報錯,僅僅是會提示“未經檢查的轉換(Unchecked assignment)”,甚至也可以將它賦給其他不同型別的帶有泛型特性的集合,只是依舊會拋出ClassCastException例外,
//型別被擦除了,保留的是型別的上限,String的上限就是Object
List list = strlist;
?
List<String> stringList2 = list;
List<Integer> intList2 = list;//你也可以把它賦給Integer型別的集合,但是當你把這個集合當成Integer的集合操作的時候,依舊會拋出ClassCastException例外
?
for (Integer i:intList2){//java.lang.ClassCastException
System.out.println(i);
}
3、泛型的定義和使用
3.1 泛型類\泛型介面
泛型類、泛型介面就是把泛型定義在類或者介面上,在用戶使用該類的時候才把型別明確下來,我們常用的集合,List,Map<K,V>,Stack……就是泛型類,在類上定義的泛型,在泛型類的方法、變數中都可以使用,
由于型別引數變數T在java泛型中僅僅是一個占位符,在傳遞引數之后才能使用,即在完成實體創建之后才能使用,所以在泛型類中,不能定義包含泛型型別的靜態變數和靜態方法,會報錯cannot be referenced from a static context,泛型類中包含泛型型別的變數和方法必須在創建了實體明確了傳遞的型別引數后才可以使用,
class Myset<T>{
private T value;
//public static T sval;//cannot be referenced from a static context
public static int sval2;
?//加入Java開發交流君樣:756584822一起吹水聊天
public Myset(){
?
}
?
public Myset(T val){
this.value = val;
}
?
public void setValue(T value) {
this.value = value;
}
?
public T getValue() {
return value;
}
?
/* public static T getSval(){//cannot be referenced from a static context
return sval;
}*/
}
Myset<String> myset = new Myset<>();
myset.setValue("12345");
System.out.println(myset.getValue());//12345
?
myset = new Myset<>("23");
?//加入Java開發交流君樣:756584822一起吹水聊天
System.out.println(myset.getClass());//class liwx.learning.Myset
3.2 泛型方法
public static <T> void PrintArray(T [] arr){
System.out.print("[");
for(T t:arr){
System.out.print(t+",");
}
System.out.println("]");
}
Integer[] a = {1,2,3,4,5,6,7};
PrintArray(a);//[1,2,3,4,5,6,7,]
3.3 泛型類的繼承
泛型類的子類有兩種繼承方式
-
子類不明確泛型類的引數變數,子類也是泛型類
-
子類明確泛型類的引數變數,子類不是泛型類
//子類不明確泛型類的引數變數,子類也是泛型類
class MyChiSet1<T> extends Myset<T>{
public MyChiSet1(){
?
}
public MyChiSet1(T val){
super(val);
}
?//加入Java開發交流君樣:756584822一起吹水聊天
}
//子類明確泛型類的引數變數,子類不是泛型類
class MyChiSet2 extends Myset<String>{
public MyChiSet2(){
?
}
public MyChiSet2(String val){
super(val);
}
}
3.4 型別通配符?及其上下限
通配符<?>和型別引數變數的區別是什么?通配符<?>是實參而不是型別形參,而且List<?>在邏輯上是List,List等所有List<具體型別實參>的父類,它的使用比型別形參T更加靈活,但傳入的通配符通常進行的是許多于具體型別無關的操作,如果涉及到具體型別相關的操作,以及回傳值,還是需要使用泛型方法T,
當我們使用?號通配符的時候,只能呼叫與物件無關的方法,不能呼叫物件與型別有關的方法,因為直到外界使用才知道具體的型別是什么,
//雖然Object是所有類的基類,但是List<Object>在邏輯上與List<Integer>等并沒有繼承關系,這個方法只能傳入List<Object>型別的資料
public static void showOList(List<Object> list){
System.out.println(list.size());
}
//同理,這個方法只能傳入List<Number>型別的資料,并不能傳入List<Integer>
public static void showList(List<Number> list){
System.out.println(list.size());
}//加入Java開發交流君樣:756584822一起吹水聊天
//使用通配符,List<?>在邏輯上是所有List<Number>,List<Integer>,List<String>……的父類,可以傳遞所有List型別的資料,但是不能在List<?>型別的資料進行于具體型別相關的操作,如add
public static void showList2(List<?> list){
System.out.println("<?>");
System.out.println(list.size());
}
//設定通配符上限,可以傳入List<Number及Number的子類>
public static void showNumList(List<? extends Number> list){
System.out.println(list.size());
}
//設定通配符上限,List<? super Number>只可以傳入List<Number及其父類>
public static boolean Compare(List<? super Number> list1,List<? super Integer> list2){
return list1.size()>list2.size();
}
List<Integer> Intgetlist = new ArrayList<>();
List<Number> Numberlist = new ArrayList<>();
//雖然Number是Integet的父類,但是傳入List,它們邏輯上沒有了繼承關系
System.out.println(Intgetlist.getClass()==Numberlist.getClass());//true
?//加入Java開發交流君樣:756584822一起吹水聊天
//showList(java.util.List<java.lang.Number>)
//List<Integer>和List<Number>邏輯上無繼承關系,所以無法呼叫
//showList(Intgetlist);//showList(java.util.List<java.lang.Number>)in FXtest cannot be applied to(java.util.List<java.lang.Integer>)
showList(Numberlist);
?
//public static void showList2(List<?> list)
//通配符List<?>在邏輯上是所有List<具體引數型別>的父類,方法可以傳入其子型別別的資料
showList2(Intgetlist);
showList2(Numberlist);
?
// public static void showNumList(List<? extends Number> list)
//當設定了通配符上限,只能傳入List<Number及其子類>的資料
List<String> Stringlist = new ArrayList<>();
showNumList(Intgetlist);
showNumList(Numberlist);//加入Java開發交流君樣:756584822一起吹水聊天
//showNumList(Stringlist);//showNumList(java.util.List<? extends java.lang.Number>)in FXtest cannot be applied to(java.util.List<java.lang.String>)
?
?
//public static boolean Compare(List<? super Number> list1,List<? super Integer> list2)
//當設定了通配符下限,List<? super Number>只能傳入List<Number及其父類>的資料,不能傳入子類Integer的List,
// 而List<? super Integer>則可以傳入其父類Number的List
//Compare(Intgetlist,Numberlist);
Compare(Numberlist,Intgetlist);
通配符的使用在邏輯上還原了泛型類傳入資料型別的引數父類子類的繼承關系,同時也可以按照需求設定通配符的上限了下限,
-
List<?>在邏輯上是所有List<具體引數型別>的父類,可對所有List資料進行操作
-
List<? extends Type>設定了通配符的上限,可對所有Type及Type的子類進行操作
-
List<? super Type>設定了通配符的下限,可對所有Type及Type的父類進行操作

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/286282.html
標籤:java
