java基本進修筆記之泛型。本站提示廣大學習愛好者:(java基本進修筆記之泛型)文章只能為提供參考,不一定能成為您想要的結果。以下是java基本進修筆記之泛型正文
泛型
將聚集中的元素限制為一個特定的類型。
術語
幾點留意:
參數化類型和原始類型互相兼容
ArrayList collection1 = new ArrayList<Integer>();//經由過程,無warning ArrayList<Integer> collection2 = new ArrayList();//經由過程,有warning
參數化類型不斟酌類型參數的繼續關系
ArrayList<String> collection3 = new ArrayList<Object>();//編譯欠亨過 ArrayList<Object> collection4 = new ArrayList<String>();//編譯欠亨過
然則
ArrayList collection5 = new ArrayList<Integer>(); ArrayList<String> collection6 = collection5;//編譯經由過程
"?"通配符
"?"表現隨意率性類型,應用"?"通配符可以援用各類參數化的類型,可以挪用與參數化有關的辦法(如size()辦法),不克不及挪用與參數化有關的辦法(如add()辦法)
通配符的擴大
限制通配符的上界限
ArrayList<? extends Number > collection1= new ArrayList<Integer >();//編譯經由過程 ArrayList<? extends Number > collection2= new ArrayList<String>();//編譯欠亨過
限制通配符的下界限
ArrayList<? super Integer > collection3= new ArrayList<Number >();//編譯經由過程 ArrayList<? super Integer > collection4= new ArrayList<String>();//編譯欠亨過
自界說泛型辦法
C++模板函數
template <class T> T add(T x, T y){ return (T)(x+y); }
而java的泛型根本上完整在編譯器中完成,用於編譯器履行類型檢討和類型斷定,然後生成通俗的非泛型的字節碼,這類完成技巧為“擦除”(erasure)。
"擦除"實例
泛型是供給給javac編譯器應用的,限制聚集的輸出類型,編譯器編譯帶類型解釋的聚集時會去失落“類型”信息。
public class GenericTest { public static void main(String[] args) { new GenericTest().testType(); } public void testType(){ ArrayList<Integer> collection1 = new ArrayList<Integer>(); ArrayList<String> collection2= new ArrayList<String>(); System.out.println(collection1.getClass()==collection2.getClass()); //二者class類型一樣,即字節碼分歧 System.out.println(collection2.getClass().getName()); //class均為java.util.ArrayList,並沒有現實類型參數信息 } }
輸入
true
java.util.ArrayList
應用反射可跳過編譯器,往某個泛型聚集參加其它類型數據。
只要援用類型能力作為泛型辦法的現實參數 例子:
public class GenericTest { public static void main(String[] args) { swap(new String[]{"111","222"},0,1);//編譯經由過程 //swap(new int[]{1,2},0,1); //編譯欠亨過,由於int不是援用類型 swap(new Integer[]{1,2},0,1);//編譯經由過程 } /*交流數組a 的第i個和第j個元素*/ public static <T> void swap(T[]a,int i,int j){ T temp = a[i]; a[i] = a[j]; a[j] = temp; } }
但留意根本類型有時可以作為實參,由於有主動裝箱和拆箱。 例子(編譯經由過程了):
public class GenericTest { public static void main(String[] args) { new GenericTest().testType(); int a = biggerOne(3,5); //int 和 double,取交為Number Number b = biggerOne(3,5.5); //String和int 取交為Object Object c = biggerOne("1",2); } //從x,y中前往y public static <T> T biggerOne(T x,T y){ return y; } }
同時,該例還注解,當實參紛歧致時,T取交集,即第一個配合的父類。 別的,假如用Number b = biggerOne(3,5.5);改成String c = biggerOne(3,5.5);則編譯報錯:
Error:(17, 29) java: 不兼容的類型: 揣摸類型不相符下限
揣摸: java.lang.Number&java.lang.Comparable<? extends java.lang.Number&java.lang.Comparable<?>>
下限: java.lang.String,java.lang.Object
然則有一點沒弄清晰,我在IDEA外面單步驟試,發明成果以下圖: 泛型調試截圖-1 不曉得b為何是Double類型的(但直接Double b吸收前往值會編譯報錯)。不曉得跟IDE有無關系,是否是IDE在debug時會顯示這個對象最准確的類型?
類型參數的類型揣摸
編譯器斷定泛型辦法的現實類型參數的進程稱為類型揣摸。
當某個類型變量只在全部參數列表的一切參數和前往值中的一處被運用了,那末依據挪用辦法時該處的現實運用類型來肯定。即直接依據挪用辦法時傳遞的參數類型或前往值來決議泛型參數的類型。 例如:
swap(new String[3],1,2) -> static <E> void swap(E[]a,int i,int j)
當某個類型變量在全部參數列表的一切參數和前往值中的多處被運用了,假如挪用辦法時這麼多處的現實運用類型都 對應統一品種型,則泛型參數的類型就是該類型。 例如:
add(3,5) -> static <T> T add(T a,T b)
當某個類型變量在全部參數列表的一切參數和前往值中的*多處被運用了,假如挪用辦法時這麼多處的現實運用類型 對應分歧的類型,且沒有前往值,則取多個參數中的最年夜交集類型,即第一個公共父類。 例如:
fill(new Integer[3],3.5) -> static <T> void fill(T a[],T v)
該例籽實際對應的類型就是Number,編譯經由過程,運轉出成績。
當某個類型變量在全部參數列表的一切參數和前往值中的多處被運用了,假如挪用辦法時這麼多處的現實運用類型對應分歧的類型,且應用有前往值,則優先斟酌前往值的類型
例如:
int x = add(3,3.5) -> static <T> T add(T a,T b)
上例編譯報錯,x類型改成float也報錯,改成Number勝利。
參數類型的類型揣摸具有傳遞性
例子:
copy(new Integer[5],new String[5]) -> static <T> void copy(T []a,T []b)
該例揣摸現實參數類型為Object,編譯經由過程.
copy(new ArrayList<String>,new Integer[5]) -> static <T> void copy(Collection<T>a,T[]b)
該例則依據參數化的ArrayList類實例將類型變量直接肯定為String類型,編譯報錯。
自界說泛型類
例子
public class GenericDao<T>{ public void add(T x){ } public T findById(int id){ return null; } public void delete(T obj){ } public void delete(int id){ } public void update(T obj){ } public T findByUserName(String name){ return null; } public <T> Set<T> findByConditions(String where){ return null; } }
留意:當一個變量被聲明為泛型時,只能被實例變量和辦法挪用(還有內嵌類型),而不克不及被靜態變量和靜態辦法挪用。由於靜態成員是被所參數化的類所同享的,所以靜態成員不該該有類級其余類型參數。
泛型辦法和泛型類的比擬
例子:
public class A<T>(){ //泛型類的成員辦法,該T受A前面的T的限制 public T memberFunc(){ return null; } //泛型辦法,這裡的T和和類A的T是分歧的 public static <T> T genericFunc(T a){ return null; } public static void main(String[] args) { //編譯欠亨過 //Integer i = A<String>().findByUserName("s"); //編譯經由過程 Set<Integer> set= A<String>().findByConditions("s"); } }
這裡Integer i = A<String>().findByUserName("s");會編譯報錯:
Error:(35, 61) java: 不兼容的類型: java.lang.String沒法轉換為java.lang.Integer
由這個例子可知,泛型辦法的T和和類A的T是分歧的。
泛型和反射
經由過程反射取得泛型的現實類型參數
把泛型變量當做辦法的參數,應用Method類的getGenericParameterTypes辦法來獲得泛型的現實類型參數 例子:
public class GenericTest { public static void main(String[] args) throws Exception { getParamType(); } /*應用反射獲得辦法參數的現實參數類型*/ public static void getParamType() throws NoSuchMethodException{ Method method = GenericTest.class.getMethod("applyMap",Map.class); //獲得辦法的泛型參數的類型 Type[] types = method.getGenericParameterTypes(); System.out.println(types[0]); //參數化的類型 ParameterizedType pType = (ParameterizedType)types[0]; //原始類型 System.out.println(pType.getRawType()); //現實類型參數 System.out.println(pType.getActualTypeArguments()[0]); System.out.println(pType.getActualTypeArguments()[1]); } /*供測試參數類型的辦法*/ public static void applyMap(Map<Integer,String> map){ } }
輸入成果:
java.util.Map<java.lang.Integer, java.lang.String> interface java.util.Map class java.lang.Integer class java.lang.String