摘要:泛型方法可以定義在普通類(lèi)和泛型類(lèi)中,泛型方法可以被修飾符修飾。泛型類(lèi)型變量會(huì)在編譯后被擦除,用第一個(gè)限定類(lèi)型替換沒(méi)有限定類(lèi)型的用替換。比如通配符類(lèi)型代表任何泛型類(lèi)型的類(lèi)型變量是和的子類(lèi)。這個(gè)類(lèi)型參數(shù)有一個(gè)子類(lèi)型限定,其自身又是一個(gè)泛型類(lèi)型。
我的博客 轉(zhuǎn)載請(qǐng)注明原創(chuàng)出處。序
之所以會(huì)想來(lái)寫(xiě)泛型相關(guān)的內(nèi)容,是因?yàn)榭吹竭@樣的一段代碼:
當(dāng)時(shí)我的內(nèi)心是這樣的:
所以就趕緊去復(fù)習(xí)了下,記錄下來(lái)?;A(chǔ)不扎實(shí),源碼看不懂啊。
泛型介紹Java 泛型(generics)是 JDK 5 中引入的一個(gè)新特性,泛型提供了編譯時(shí)類(lèi)型安全檢測(cè)機(jī)制,該機(jī)制允許程序員在編譯時(shí)檢測(cè)到非法的類(lèi)型。泛型的本質(zhì)是參數(shù)化類(lèi)型,也就是說(shuō)所操作的數(shù)據(jù)類(lèi)型被指定為一個(gè)參數(shù),在Java集合框架里使用的非常廣泛。
定義的重點(diǎn)是提供了編譯時(shí)類(lèi)型安全檢測(cè)機(jī)制。比如有這樣的一個(gè)泛型類(lèi):
public class Generics{ private T value; public T getValue() { return value; } public void setValue(T value) { this.value = value; } }
然后寫(xiě)這樣一個(gè)類(lèi):
public class Generics { private Object value; public Object getValue() { return value; } public void setValue(Object value) { this.value = value; } }
它們同樣都能存儲(chǔ)所有值,但是泛型類(lèi)有編譯時(shí)類(lèi)型安全檢測(cè)機(jī)制:
泛型類(lèi)一個(gè)類(lèi)定義了一個(gè)或多個(gè)類(lèi)型變量,那么就是泛型類(lèi)。語(yǔ)法是在類(lèi)名后用尖括號(hào)括起來(lái),類(lèi)型變量寫(xiě)在里面用逗號(hào)分開(kāi)。然后就可以在方法的返回類(lèi)型、參數(shù)和域、局部變量中使用類(lèi)型變量了,但是不能在有static修飾符修飾的方法或域中使用。例子:
類(lèi)定義參考上文例子 使用形式 Generics泛型方法generics = new Generics (); 后面尖括號(hào)內(nèi)的內(nèi)容在Jdk7以后可以省略 Generics generics = new Generics<>();
一個(gè)方法定義了一個(gè)或多個(gè)類(lèi)型變量,那么就是泛型方法。語(yǔ)法是在方法修飾符后面、返回類(lèi)型前面用尖括號(hào)括起來(lái),類(lèi)型變量寫(xiě)在里面用逗號(hào)分開(kāi)。泛型方法可以定義在普通類(lèi)和泛型類(lèi)中,泛型方法可以被static修飾符修飾。
例子:
private void out(U u) { System.out.println(u); } 調(diào)用形式, Test.類(lèi)型變量的限定out("test"); 大部分情況下 都可以省略,編譯器可以推斷出來(lái)類(lèi)型 Test.out("test");
有時(shí)候我們會(huì)有希望限定類(lèi)型變量的情況,比如限定指定的類(lèi)型變量需要實(shí)現(xiàn)List接口,這樣我們就可以在代碼對(duì)類(lèi)型變量調(diào)用List接口里的方法,而不用擔(dān)心會(huì)沒(méi)有這個(gè)方法。
public class Generics{ private T value; public T getValue() { return value; } public void setValue(T value) { this.value = value; } public void add(Object u) { value.add(u); } public static void main(String[] args) { Generics generics = new Generics<>(); generics.setValue(new ArrayList<>()); generics.add("ss"); System.out.println(generics.getValue()); } }
限定的語(yǔ)法是在類(lèi)型變量的后面加extends關(guān)鍵字,然后加限定的類(lèi)型,多個(gè)限定的類(lèi)型要用&分隔。類(lèi)型變量和限定的類(lèi)型可以是類(lèi)也可以是接口,因?yàn)镴ava中類(lèi)只能繼承一個(gè)類(lèi),所以限定的類(lèi)型是類(lèi)的話(huà)一定要在限定列表的第一個(gè)。
類(lèi)型擦除類(lèi)型擦除是為了兼容而搞出來(lái)的,大意就是在虛擬機(jī)里是沒(méi)有泛型類(lèi)型,泛型只存在于編譯期間。泛型類(lèi)型變量會(huì)在編譯后被擦除,用第一個(gè)限定類(lèi)型替換(沒(méi)有限定類(lèi)型的用Object替換)。上文中的Generics
public class Generics { private Object value; public Object getValue() { return value; } public void setValue(Object value) { this.value = value; } }
之所以我們能設(shè)置和返回正確的類(lèi)型是因?yàn)榫幾g器自動(dòng)插入了類(lèi)型轉(zhuǎn)換的指令。
public static void main(String[] args) { Genericsgenerics = new Generics<>(); generics.setValue("ss"); System.out.println(generics.getValue()); } javac Generics.java javap -c Generics 編譯后的代碼 public static void main(java.lang.String[]); Code: 0: new #3 // class generics/Generics 3: dup 4: invokespecial #4 // Method " ":()V 7: astore_1 8: aload_1 9: ldc #5 // String ss 11: invokevirtual #6 // Method setValue:(Ljava/lang/Object;)V 14: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream; 17: aload_1 18: invokevirtual #8 // Method getValue:()Ljava/lang/Object; 21: checkcast #9 // class java/lang/String 24: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 27: return
我們可以看到在21行插入了一條類(lèi)型轉(zhuǎn)換的指令。
類(lèi)型擦除還帶來(lái)了另一個(gè)問(wèn)題,如果我們有一個(gè)類(lèi)繼承了泛型類(lèi)并重寫(xiě)了父類(lèi)的方法:
public class SubGenerics extends Generics{ @Override public void setValue(String value) { System.out.println(value); } public static void main(String[] args) { Generics generics = new SubGenerics(); generics.setValue("ss"); } }
因?yàn)轭?lèi)型擦除所以SubGenerics實(shí)際上有兩個(gè)setValue方法,SubGenerics自己的setValue(String value)方法和從Generics繼承來(lái)的setValue(Object value)方法。例子中的generics引用的是SubGenerics對(duì)象,所以我們希望調(diào)用的是SubGenerics.setValue。為了保證正確的多態(tài)性,編譯器在SubGenerics類(lèi)中生成了一個(gè)橋方法:
public void setValue(Object value) { setValue((String) value); }
我們可以編譯驗(yàn)證下:
Compiled from "SubGenerics.java" public class generics.SubGenerics extends generics.Generics{ public generics.SubGenerics(); Code: 0: aload_0 1: invokespecial #1 // Method generics/Generics." ":()V 4: return public void setValue(java.lang.String); Code: 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: aload_1 4: invokevirtual #3 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 7: return public static void main(java.lang.String[]); Code: 0: new #4 // class generics/SubGenerics 3: dup 4: invokespecial #5 // Method " ":()V 7: astore_1 8: aload_1 9: ldc #6 // String ss 11: invokevirtual #7 // Method generics/Generics.setValue:(Ljava/lang/Object;)V 14: return public void setValue(java.lang.Object); Code: 0: aload_0 1: aload_1 2: checkcast #8 // class java/lang/String 5: invokevirtual #9 // Method setValue:(Ljava/lang/String;)V 8: return }
引用《Java核心技術(shù) 卷一》約束與局限性總之,需要記住有關(guān) Java 泛型轉(zhuǎn)換的事實(shí):
1.虛擬機(jī)中沒(méi)有泛型,只有普通的類(lèi)和方法。
2.所有的類(lèi)型參數(shù)都用它們的限定類(lèi)型替換。
3.橋方法被合成來(lái)保持多態(tài)。
4.為保持類(lèi)型安全性,必要時(shí)插人強(qiáng)制類(lèi)型轉(zhuǎn)換。
類(lèi)型變量不能是基本變量,比如int,double等。應(yīng)該使用它們的包裝類(lèi)Integer,Double。
虛擬機(jī)中沒(méi)有泛型,所以不能使用運(yùn)行時(shí)的類(lèi)型查詢(xún),比如 if (generics instanceof Generics
因?yàn)轭?lèi)型擦除的原因,不能創(chuàng)建泛型類(lèi)數(shù)組,比如Generics
不能實(shí)例化類(lèi)型變量,比如new T(...) new T[...] 或 T.class
通配符類(lèi)型通配符類(lèi)型和上文中的類(lèi)型變量的限定有些類(lèi)似,區(qū)別是通配符類(lèi)型是運(yùn)用在聲明的時(shí)候而類(lèi)型變量的限定是在定義的時(shí)候。比如通配符類(lèi)型Generics extends List>代表任何泛型Generics類(lèi)型的類(lèi)型變量是List和List的子類(lèi)。
Generics extends List> generics = new Generics
不過(guò)這樣聲明之后Generics的方法也發(fā)生了變化,變成了
這樣就導(dǎo)致了不能調(diào)用setValue方法
而getValue方法是正常的
超類(lèi)型限定通配符限定還可以限定超類(lèi),比如通配符類(lèi)型Generics super ArrayList>代表任何泛型Generics類(lèi)型的類(lèi)型變量是ArrayList和ArrayList的超類(lèi)。
Generics super ArrayList> generics = new Generics();
同樣的,Generics的方法也發(fā)生了變化,變成了
調(diào)用getValue方法只能賦值給Object變量
調(diào)用setValue方法只能傳入ArrayList和ArrayList的子類(lèi),超類(lèi)List,Object等都不行
反射和泛型雖然因?yàn)轭?lèi)型擦除,在虛擬機(jī)里是沒(méi)有泛型的。不過(guò)被擦除的類(lèi)還是保留了一些關(guān)于泛型的信息,可以使用反射相關(guān)的Api來(lái)獲取。
類(lèi)似地,看一下泛型方法
public static
這是擦除后
public static Comparable min(Coniparable[] a)
可以使用反射 API 來(lái)確定:
這個(gè)泛型方法有一個(gè)叫做T的類(lèi)型參數(shù)。
這個(gè)類(lèi)型參數(shù)有一個(gè)子類(lèi)型限定, 其自身又是一個(gè)泛型類(lèi)型。
這個(gè)限定類(lèi)型有一個(gè)通配符參數(shù)。
這個(gè)通配符參數(shù)有一個(gè)超類(lèi)型限定。
這個(gè)泛型方法有一個(gè)泛型數(shù)組參數(shù)。
后記周一就建好的草稿,到了星期天才寫(xiě)好,還是刪掉了一些小節(jié)情況下,怕是拖延癥晚期了......不過(guò)也是因?yàn)榉盒偷膬?nèi)容夠多,雖然日常業(yè)務(wù)里很少自己去寫(xiě)泛型相關(guān)的代碼,但是在閱讀類(lèi)庫(kù)源碼時(shí)要是不懂泛型就寸步難行了,特別是集合相關(guān)的。這次的大部分內(nèi)容都是《Java核心技術(shù) 卷一》里的,這可是本關(guān)于Java基礎(chǔ)的好書(shū)。不過(guò)還是老規(guī)矩,光讀可不行,還是要用自己的語(yǔ)言記錄下來(lái)。眾所周知,人類(lèi)的本質(zhì)是復(fù)讀機(jī),把好書(shū)里的內(nèi)容重復(fù)一遍,就等于我也有責(zé)任了!
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/71738.html
摘要:哪吒社區(qū)技能樹(shù)打卡打卡貼函數(shù)式接口簡(jiǎn)介領(lǐng)域優(yōu)質(zhì)創(chuàng)作者哪吒公眾號(hào)作者架構(gòu)師奮斗者掃描主頁(yè)左側(cè)二維碼,加入群聊,一起學(xué)習(xí)一起進(jìn)步歡迎點(diǎn)贊收藏留言前情提要無(wú)意間聽(tīng)到領(lǐng)導(dǎo)們的談話(huà),現(xiàn)在公司的現(xiàn)狀是碼農(nóng)太多,但能獨(dú)立帶隊(duì)的人太少,簡(jiǎn)而言之,不缺干 ? 哪吒社區(qū)Java技能樹(shù)打卡?【打卡貼 day2...
摘要:基礎(chǔ)問(wèn)題的的性能及原理之區(qū)別詳解備忘筆記深入理解流水線(xiàn)抽象關(guān)鍵字修飾符知識(shí)點(diǎn)總結(jié)必看篇中的關(guān)鍵字解析回調(diào)機(jī)制解讀抽象類(lèi)與三大特征時(shí)間和時(shí)間戳的相互轉(zhuǎn)換為什么要使用內(nèi)部類(lèi)對(duì)象鎖和類(lèi)鎖的區(qū)別,,優(yōu)缺點(diǎn)及比較提高篇八詳解內(nèi)部類(lèi)單例模式和 Java基礎(chǔ)問(wèn)題 String的+的性能及原理 java之yield(),sleep(),wait()區(qū)別詳解-備忘筆記 深入理解Java Stream流水...
摘要:基礎(chǔ)問(wèn)題的的性能及原理之區(qū)別詳解備忘筆記深入理解流水線(xiàn)抽象關(guān)鍵字修飾符知識(shí)點(diǎn)總結(jié)必看篇中的關(guān)鍵字解析回調(diào)機(jī)制解讀抽象類(lèi)與三大特征時(shí)間和時(shí)間戳的相互轉(zhuǎn)換為什么要使用內(nèi)部類(lèi)對(duì)象鎖和類(lèi)鎖的區(qū)別,,優(yōu)缺點(diǎn)及比較提高篇八詳解內(nèi)部類(lèi)單例模式和 Java基礎(chǔ)問(wèn)題 String的+的性能及原理 java之yield(),sleep(),wait()區(qū)別詳解-備忘筆記 深入理解Java Stream流水...
摘要:基礎(chǔ)問(wèn)題的的性能及原理之區(qū)別詳解備忘筆記深入理解流水線(xiàn)抽象關(guān)鍵字修飾符知識(shí)點(diǎn)總結(jié)必看篇中的關(guān)鍵字解析回調(diào)機(jī)制解讀抽象類(lèi)與三大特征時(shí)間和時(shí)間戳的相互轉(zhuǎn)換為什么要使用內(nèi)部類(lèi)對(duì)象鎖和類(lèi)鎖的區(qū)別,,優(yōu)缺點(diǎn)及比較提高篇八詳解內(nèi)部類(lèi)單例模式和 Java基礎(chǔ)問(wèn)題 String的+的性能及原理 java之yield(),sleep(),wait()區(qū)別詳解-備忘筆記 深入理解Java Stream流水...
閱讀 2351·2021-11-24 10:18
閱讀 2812·2021-11-19 09:59
閱讀 1767·2019-08-30 15:53
閱讀 1248·2019-08-30 15:53
閱讀 1120·2019-08-30 14:19
閱讀 2540·2019-08-30 13:14
閱讀 3085·2019-08-30 13:00
閱讀 2081·2019-08-30 11:11