泛型的本質是為了參數化類型(在不創建新的類型的情況下,透過泛型指定的不同類型來控制形參具體限制的類型)。
先看以下的例子:
我們以前學過的數組,只能存放指定類型的元素。如:int[] array=new int[10];String[] array=new String[10];
而Object類別是所有類別的父類,那麼我們是否可以建立Obj陣列呢?
class Myarray{ public Object[] array=new Object[10]; public void setVal(int pos,Object val){ this.array[pos]=val; } public Object getPos(int pos){ return this.array[pos]; } } public class TestDemo{ public static void main(String[] args) { Myarray myarray=new Myarray(); myarray.setVal(1,0); myarray.setVal(2,"shduie");//字符串也可以存放 String ret=(String)myarray.getPos(2);//虽然我们知道它是字符串类型,但是还是要强制类型转换 System.out.println(ret); } }
以上程式碼實作後,我們發現:
任何類型的資料都能存放
2號下標本來就是字串,但是必須進行強制型別轉換
以此引出泛型,泛型的目的是:指定目前的容器要持有什麼類型的對象,讓編譯器自己去檢查。
class 泛型類別名稱d9a850d276535a6ae003bca4dfab1b3b{
//這裡可以使用型別參數
}
泛型的使用:
泛型類別03354cf2845c31f37c08275aed8db581 變數名稱=new 泛型類別03354cf2845c31f37c08275aed8db581(建構方法實參)
MyArray list=new MyArraya8093152e673feb7aba1828c43532094();
【註】
#型別後的a8093152e673feb7aba1828c43532094代表佔位符,表示目前類別是泛型類別
在實例化泛型時,a8093152e673feb7aba1828c43532094中不能是簡單的類型,需要是包裝類別
a8093152e673feb7aba1828c43532094不參與泛型的型別組成
不能new泛型的陣列
使用泛型不需要進行強制型別轉換
#一個簡單的泛型:
//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型 //在实例化泛型类时,必须指定T的具体类型 public class Test<T>{ //key这个成员变量的类型为T,T的类型由外部指定 private T key; public Test(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定 this.key = key; } public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定 return key; } }
擦除機制:編譯時會將a8093152e673feb7aba1828c43532094中的型別擦除掉,所以a8093152e673feb7aba1828c43532094中的東西不參與類型的組成。會將T擦除為Object。
為什麼不能實例化泛型類型的陣列?
陣列和泛型之間的一個重要區別是它們如何強制執行類型檢查。數組在運行時儲存和檢查類型信息,而泛型是在編譯時檢查類型錯誤。
傳回的Object數組裡面,可能存放著任何類型的數據,如string,透過int類型的數組來接收,編譯器認為是不安全的。
語法:
class 泛型類別名稱3c77d6081ac41fcac8a1ee971a62484e{
# }
範例:
public class MyArray{} //E只能是Number或Number的子類別
##【註】沒有指定邊界的E,可以看作E extends Object4、萬用字元?用於在泛型的使用,即為通配符。通配符用來解決反泛型無法協變的問題。 如下兩段程式碼:public class MyArray587c6b841f9775ac09a42ccb3bfcf6bc>{}
//E一定實作了Comparable介面的類別
代码一: public static<T> void printList1(ArrayList<T> list){ for(T x:list){ System.out.println(x); } } 代码二: public static<T> void printList2(ArrayList<?> list){ for(Object x:list){ System.out.println(x); } }程式碼2中使用了通配符,和程式碼1相比,此時傳入程式碼1的具體是什麼資料類型,我們是不清楚的。 (1)通配符的上界語法:
17c0f368e92dd93822c5c4ac304197ad //可以傳入的實參類型為Number或Number的子類別範例:對於以下關係,我們需要寫一個方法來列印儲存了Animal或Animal子類別的list。
Animal程式碼一:Cat extends Animal
Dog extends Animal
public static <t extends Animal> void print1(List<T> list>{ for(T animal:list){ System.out.println(animal);//调用了T的toString } }此時T型別是Animal的子類或自己。 程式碼二:透過通配符實作
public static void print2(List<? extends Animal> list){ for(Animal animal:list){ Syatem.out.println(animal);//调用了子类的toString方法 } }兩種程式碼的差異:
#//需要使用通配符來確定父子類型MyArrayLista2b037db85f4e1df0e812b9647ac55a8是MyArrayListc0f559cc8d56b43654fcbe4aa9df7b4a或MyArrayListeafb63d086dd6c9bd19609d76bcc2869的父類別##MyArrayList<?> ;? extends Number>的父類別
ArrayList<Integer> arrayList1 = new ArrayList<>(); ArrayList<Double> arrayList2 = new ArrayList<>(); List<? extends Number> list = arrayList1; //list.add(1,1);//报错,此时list的引用的子类对象有很多,再添加的时候,任何子类型都可以,为了安全,java不让这样进行添加操作。 Number a = list.get(0);//可以通过 Integer i = list.get(0);//编译错误,只能确定是Number子类【註】
6a4551997e2af77c2331ddf9cfbb1e38//可以傳入的參數型別是Integer或Integer的父類別
通配符下界的父子類別關係:
MyArrayList474555482eb79f9e4e676a9012437c1d是MyArrayList1c0477c859e3a4cbd4faa63087d62a35的父类类型
MyArrayLista10469e4b40f04339ce5bebfbf5abb4c是MyArrayList474555482eb79f9e4e676a9012437c1d的父类
通配符下界适合写入元素,不适合读取。
在Java中,由于基本类型不是继承自Object,为了在泛型中可以支持基本类型,每个基本类型都对应了一个包装类。除了Integer和Character,其余基本类型的包装类都是首字母大写。
拆箱和装箱:
int i=10; //装箱操作,新建一个Integer类型对象,将i的值放入对象的某个属性中 Integer ii=i; //自动装箱 //Integer ii=Integer.valueOf(i); Integer ij= new Integer(i);//显示装箱 //拆箱操作,将Integer对象中的值取出,放到一个基本数据类型中 int j=ii.intValue();//显示的拆箱 int jj=ii;//隐式的拆箱
以上是Java 泛型與包裝類別範例分析的詳細內容。更多資訊請關注PHP中文網其他相關文章!