您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“Java泛型的概念和Type類型體系”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“Java泛型的概念和Type類型體系”吧!
1 JAVA的Type類型體系
先了解下java的Type類型體系(類的類=>類型),Type是所有類型(原生類型-Class、參數(shù)化類型-Parameterizedtype、數(shù)組類型-GenericArrayType、類型變量-TypeVariable、基本類型-Class)的共同接口;前兩篇反射和注解講到的Class
Type下面又有四個(gè)子接口類ParameterizedType、TypeVariable、GenericArrayType、WildcardType
List
具體化泛型中的類型時(shí),可以使用 ? extends 或 ? super來表示繼承關(guān)系;如List,而里面的 ? 稱為通配符類型WildcardType
GenericArrayType 表示一種元素類型是ParameterizedType(參數(shù)化類型)或者TypeVariable(類型變量)的數(shù)組類型,如T[] 或者 List
注解是JDK1.5才出現(xiàn)了的,為了表示被注解的類型的,加入AnnotatedElement類型,字面意思就是被注解的元素。JDK1.8又有了AnnotatedType將Type和被注解元素的概念關(guān)聯(lián)起來。
AnnotatedType也有四個(gè)子接口,和Type的四個(gè)子接口一一對(duì)應(yīng),如:ParameterizedType類型被注解則被編譯器解析成AnnotatedParameterizedType: @AnTest("list")List
2 泛型的概念
Java 泛型(generics)是JDK1.5中引入的一個(gè)新特性,其本質(zhì)是參數(shù)化類型,解決不確定具體對(duì)象類型的問題;其所操作的數(shù)據(jù)類型被指定為一個(gè)參數(shù)(type parameter)這種參數(shù)類型可以用在類、接口和方法的創(chuàng)建中,分別稱為泛型類、泛型接口、泛型方法
泛型: 把類型明確的工作推遲到創(chuàng)建對(duì)象或調(diào)用方法的時(shí)候才去明確的特殊的類型
3 泛型類和泛型方法的示例
泛型類的定義
public class MainTest<T> { private T param; } public static void main(String[] args){ MainTest<String> data = new MainTest<String>(){}; ParameterizedType genType1 = (ParameterizedType)data.getClass().getGenericSuperclass(); }
泛型方法的定義
public class MainTest{ public static void main(String[] args){ printData("siting"); } static <T> T printData(T t){ System.out.println(t); return t; } }
接口和抽象類都可以使用泛型
4 類型擦除
創(chuàng)建泛型的實(shí)例時(shí),jvm是會(huì)把具體類型擦除的;編譯生成的字節(jié)碼中不包含泛型中的類型參數(shù),即ArrayList
public class MainTest { public static void main(String[] args){ List<String> strArr = new ArrayList<>(); List<Integer> intArr = new ArrayList<>(); Type strClazz = strArr.getClass(); Type intClazz = intArr.getClass(); } }
查看編譯后的字節(jié)碼文件是如何表示的: idea菜單 -> view -> show ByteCode
public class MainTest<T> { T param; public static void main(String[] args){ MainTest<String> test = new MainTest<>(); test.setParam("siting"); } public T getParam() { return param; } public void setParam(T param) { this.param = param; } } public class com/MainTest { ...省略 public static main([Ljava/lang/String;)V L0 LINENUMBER 7 L0 NEW com/MainTest DUP INVOKESPECIAL com/MainTest.<init> ()V ASTORE 1 L1 LINENUMBER 8 L1 ALOAD 1 LDC "siting" // 調(diào)用類型擦除后的setParam(Object) INVOKEVIRTUAL com/MainTest.setParam (Ljava/lang/Object;)V L2 ...省略//getParam 的返回值是Object public getParam()Ljava/lang/Object; L0 LINENUMBER 10 L0 ALOAD 0 GETFIELD com/MainTest.param : Ljava/lang/Object; ARETURN ...省略//setParam 的入?yún)⑹荗bject public setParam(Ljava/lang/Object;)V L0 LINENUMBER 11 L0 ALOAD 0 ALOAD 1 PUTFIELD com/MainTest.param : Ljava/lang/Object; RETURN ... }
可以看出T(String)都被轉(zhuǎn)換為Object類型,最初的初始化的String不見了
5 泛型的繼承
子類可以指定父類的泛型參數(shù),可以是已知類(Integer、String等),也可以用子類自己的泛型參數(shù)指定
泛型被繼承時(shí),且指定父類泛型參數(shù),則額外生成的ParameterizedType類型作為子類的父類;如果沒有指定父類泛型參數(shù),則直接繼承原生類型
public class MainTest<T> { T param; static public class SubTest1 extends MainTest<String>{} static public class SubTest2<R> extends MainTest<R>{} //SubTest3繼承的時(shí)原生類型 static public class SubTest3 extends MainTest{} }
6 泛型變量TypeVariable
(先臨時(shí)定義一個(gè)名稱,Test
public class MainTest<T> { List<T> param; public static void main(String[] args) throws Exception{ Class clazz = MainTest.class; TypeVariable[] typeVariable = clazz.getTypeParameters(); // 1 Field field = clazz.getDeclaredField("param"); ParameterizedType arrayType = (ParameterizedType)field.getGenericType(); // interface List<E> 的泛型類型E被T,具體化,因此其被識(shí)別為 TypeVariable TypeVariable variable1 = (TypeVariable)arrayType.getActualTypeArguments()[0]; // 2 ParameterizedType type = (ParameterizedType)SubTest.class.getGenericSuperclass(); TypeVariable variable2 = (TypeVariable)type.getActualTypeArguments()[0]; } static class SubTest<R> extends MainTest<R>{} }
7 參數(shù)化類型ParameterizedType
public interface ParameterizedType extends Type { //獲取實(shí)際參數(shù),List<String>里的String; 如果是List<T>則是TypeVariable類型 Type[] getActualTypeArguments(); // 獲取原始類型List<String> -> List<E> Type getRawType(); Type getOwnerType(); }
需要注意的點(diǎn),我們不能直接獲取指定具體參數(shù)的泛型的類型,如Class clazz = List
public class MainTest<T> { public static void main(String[] args){ MainTest<String> str = new MainTest<String>(); Class variable = str.getClass(); Type genType1 = variable.getGenericSuperclass(); } }
被具體參數(shù)化的泛型才能被編譯器識(shí)別為ParameterizedType類型,有三種方式獲取ParameterizedType類型
// 1 子類繼承泛型時(shí),指定具體參數(shù)(可以是String等已知類型,也可以是子類的泛型參數(shù)) // 2 獲取在類內(nèi)部定義的泛型屬性,需指定具體泛型參數(shù) // 3 局部代碼,可以通過泛型的匿名內(nèi)部子類(需指定具體泛型參數(shù))獲取ParameterizedType類型 public class MainTest<T> { List<T> list; public static void main(String[] args) throws NoSuchFieldException { SubTest<String> str = new SubTest<>(); // 方式一 Class variable = str.getClass(); // 父類是(521)ParameterizedType類型 ParameterizedType genType = (ParameterizedType)variable.getGenericSuperclass(); // (521)ParameterizedType類型的原生類型是(479)class com.MainTest Type clazz = genType.getRawType(); //MainTest.class 的原生類型是 (479)class com.MainTest Class rawClazz = MainTest.class; //方式二,泛型屬性 Field field = rawClazz.getDeclaredField("list"); //屬性list 類型是(546)ParameterizedType類型List<T> ParameterizedType fieldType = (ParameterizedType)field.getGenericType(); // 方式三 MainTest<String> sub3 = new MainTest<String>(){}; // clazz3是匿名子類 Class clazz3 = sub3.getClass(); //父類是(555)ParameterizedType類型 ParameterizedType genType3 = (ParameterizedType) clazz3.getGenericSuperclass(); // (555)ParameterizedType類型的原生類型是(479)class com.MainTest Type type3 = genType3.getRawType(); } public static class SubTest<R> extends MainTest<R>{ } }
8 通配符(WildcardType)
無邊界通配符:無界通配符 ? 可以適配任何引用類型:
當(dāng)方法參數(shù)需要傳入一個(gè)泛型時(shí),而且無法確定其類型時(shí)。直接使用無具體泛型變量的泛型,容易造成安全隱患;若在方法代碼里進(jìn)行類型轉(zhuǎn)換,極容易出現(xiàn)ClassCastException錯(cuò)誤
那泛型變量用Object代替不就行了?但是泛型類+具體參數(shù)轉(zhuǎn)變的ParameterizedType(參數(shù)化類型)是不存在繼承關(guān)系;即Object是String的父類,但是List 和List
public static void print(List list){} ----->>> public static void print(List<?> list){}
無界通配符可以匹配任意類型;但是在使用?時(shí),不能給泛型類的變量設(shè)置值,因?yàn)槲覀儾恢谰唧w類型是什么;如果強(qiáng)行設(shè)置新值,后面的讀容易出現(xiàn)ClassCastException錯(cuò)誤。因此編譯器限制了**通配符 ?**的泛型只能讀不能寫
上界限定通配符 < ? extends E>
想接收一個(gè)List集合,它只能操作數(shù)字類型的元素【Float、Integer、Double、Byte等數(shù)字類型都行】,怎么做?可以使用List,表明List里的元素都是Number的子類
public static void print(List<? extends Number> list) { Number n = new Double("1.0"); list.add(n); Number tmp = list.get(0); }
圖片里可以看出,存在上界通配符,因?yàn)榫唧w類型不確定,也是只能讀不能寫的
下界限定通配符 < ? super E>
class Parent{ } class Child extends Parent{ } public class MainTest<T> { T param; public static void main(String[] args){ MainTest<? super Child> parent_m = new MainTest<>(); parent_m.setParam(new Child()); Object parent = parent_m.getParam(); } public T getParam() { return param; } public void setParam(T param) { this.param = param; } }
如果定義了通配符是誰的父類,則是下界限定通配符;此類通配符可讀可寫,轉(zhuǎn)成任意父類都不會(huì)出現(xiàn)ClassCastException錯(cuò)誤。
個(gè)人猜想:難道是因?yàn)橥ㄅ浞蜕辖缦薅ㄍㄅ浞姆盒? 向下轉(zhuǎn)型容易出現(xiàn)ClassCastException錯(cuò)誤,而下界限定通配符向上轉(zhuǎn)型不會(huì)出現(xiàn)ClassCastException錯(cuò)誤,因此java規(guī)范限制前者編譯出錯(cuò),而后面編譯通過?
9 泛型數(shù)組(GenericArrayType)
public interface GenericArrayType extends Type { //獲得這個(gè)數(shù)組元素類型,即獲得:A<T>(A<T>[])或 T(T[]) Type getGenericComponentType(); }
GenericArrayType,泛型數(shù)組,描述的是ParameterizedType類型以及TypeVariable類型數(shù)組,即形如:Test
public class MainTest<T> { T[] param; public static void main(String[] args) throws Exception{ Class clazz = MainTest.class; Field field = clazz.getDeclaredField("param"); GenericArrayType arrayType = (GenericArrayType)field.getGenericType(); TypeVariable variable = (TypeVariable) arrayType.getGenericComponentType(); } }
到此,相信大家對(duì)“Java泛型的概念和Type類型體系”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。