摘要:通過方法返回值來確定具體泛型類型。泛型類定義的泛型聲明在整個(gè)類定義中都有效。相同類型參數(shù)的泛型類的關(guān)系取決于泛型類自身的繼承體系結(jié)構(gòu)。
首先,明確一點(diǎn)的是:java的泛型是擦除的。
即系,泛型只存在于編譯時(shí),編譯后都是Object類型(也不全是,見后面解析,運(yùn)行時(shí)也能獲得一定的泛型信息)。
泛型對(duì)于Java編程的作用:
(
最主要:增加編譯時(shí)的類型安全檢查。
其次:你說它能做到抽取不同類型的共同代碼的話,可能就只適用于容器類。容器類中適合存放不同的類型,而且不管存放的類型是什么都適用。但是要求存放的類型是要一致。
)
泛型的所做的實(shí)質(zhì)工作:
編譯時(shí),在泛型類的入口(如:方法參數(shù)),會(huì)進(jìn)行編譯時(shí)的類型安全檢查(子類,父類關(guān)系檢查,是否是安全的向上轉(zhuǎn)型)。
而且編譯器會(huì)在編譯時(shí)增加語法糖,對(duì)泛型類的出口(如: 返回值),編譯器會(huì)對(duì)字節(jié)碼自動(dòng)增加類型轉(zhuǎn)換的代碼。
編譯前的泛型類:
public static void main(String[] args) { Mapmap = new HashMap (); map.put("hello", "你好"); map.put("how are you?", "吃了沒?"); System.out.println(map.get("hello")); System.out.println(map.get("how are you?")); }
把上面的代碼編譯成class文件后,再用字節(jié)碼反編譯工具進(jìn)行反編譯后:
public static void main(String[] args) { Map map = new HashMap(); map.put("hello", "你好"); map.put("how are you?", "吃了沒?"); System.out.println((String) map.get("hello")); System.out.println((String) map.get("how are you?")); }
在運(yùn)行時(shí),
(重要?。┛偨Y(jié):擦除法所謂的擦除,僅僅是對(duì)字節(jié)碼中方法中的方法體的Code屬性中的字節(jié)碼進(jìn)行擦除(即方法體中的代碼沒有泛型),但是實(shí)際上的元數(shù)據(jù)(類定義,方法定義,字段定義)的Signature中還是保存著 具體參數(shù)化類型 信息。
上面描述的具體參數(shù)化類型信息,可以供用戶查詢得到。
(如:類定義MyList implements List
在編譯時(shí),
我們知道編譯器時(shí)掌握泛型信息的。
那么問題剩下: 編譯器如何依據(jù)泛型信息在編譯時(shí)進(jìn)行類型安全檢查。
第一點(diǎn):
在(泛型類定義,泛型接口方法和函數(shù))定義中,編譯器如何確定具體泛型類型。
對(duì)于泛型類定義:
(通常一個(gè)變量引用的聲明類型來決定,或者實(shí)例化對(duì)象時(shí)指定)
如果泛型類定義是(如:public interface Map
現(xiàn)在有一個(gè)變量聲明,Map
又如,有一個(gè)實(shí)例化對(duì)象 new HashMap
對(duì)于 泛型接口方法和函數(shù)(包括實(shí)例化對(duì)象時(shí)的構(gòu)造函數(shù)) :
編譯器確定具體泛型類型是通過方法簽名中的 方法參數(shù) 或者 方法返回值。
(1)通過 方法參數(shù) 來確定 具體泛型類型。
(2)通過 方法返回值 來確定 具體泛型類型。
(3)構(gòu)造函數(shù)
java.util.ArrayList.ArrayList(Collection extends E> c)
第二點(diǎn):
在(泛型類定義,泛型接口方法和函數(shù))中的泛型聲明在編譯器中的作用域(泛型聲明的影響范圍)和對(duì)應(yīng)關(guān)系。
泛型類定義的泛型聲明在整個(gè)類定義中都有效。
如果在泛型類定義中有泛型聲明,(如:public interface Map
泛型接口方法和函數(shù)的泛型聲明在方法體和方法簽名有效。如果在泛型接口方法和函數(shù)上聲明了自己的泛型聲明則獨(dú)立開辟一個(gè)新的作用域,與類定義的泛型聲明區(qū)分開。
public class CopyOfGenericCollection { // 在泛型接口方法和函數(shù)中新開辟一個(gè)泛型聲明作用域 // 這里的泛型聲明A和B,與類定義中的A和B 是沒有關(guān)系。 public Map unmodifiableMap1(Map super A, ? extends B> m) { return null; } // 沿用泛型類定義的泛型聲明 public Map unmodifiableMap2(Map super A, ? extends B> m) { return null; } }
// 測(cè)試
public class Test { public static void main(String[] args) { CopyOfGenericCollectionextend = new CopyOfGenericCollection (); // 可以看到map1變量的類型為Map
最后
編譯的類型安全檢查,最基本的就是類型轉(zhuǎn)換,我們知道類型向上轉(zhuǎn)型是安全。
所以要使包含泛型的代碼中通過編譯器的編譯,我們要求賦值語句的都是,子類賦值父類,這就引入了 泛型系統(tǒng)的類型繼承關(guān)系。
相同類型參數(shù)的泛型類的關(guān)系取決于泛型類自身的繼承體系結(jié)構(gòu)。
即List
當(dāng)泛型類的類型聲明中使用了通配符的時(shí)候, 其子類型可以在兩個(gè)維度上分別展開。
如對(duì)Collection extends Number>來說,其子類型可以在Collection這個(gè)維度上展開,即List extends Number>和Set extends Number>等;
也可以在Number這個(gè)層次上展開,即Collection
如此循環(huán)下去,ArrayList
如果泛型類中包含多個(gè)類型參數(shù)(如:Map),則對(duì)于每個(gè)類型參數(shù)分別應(yīng)用上面的規(guī)則。
Collection extends Number> col_number = null; List extends Number> list_number = null; Set extends Number> set_number = null; Collectioncol_double = null; Collection col_integer = null; HashSet hashSet_double = null; ArrayList arrayList_integer = null; col_number = list_number; col_number = set_number; col_number = col_double; col_number = col_integer; col_number = hashSet_double; col_number = arrayList_integer; Collection super Number> col_number_super = null; Collection super List> col_list_super = null; Collection col_col_super = null; Collection col_arrayList_super = null; // col_number_super = col_number;// compile error // col_number = col_number_super;// compile error col_number_super = (Collection super Number>) col_number; col_number = (Collection extends Number>) col_number_super; col_list_super = col_col_super; // col_col_super = col_list_super; // compile error // col_list_super = col_arrayList_super; // compile error Map extends Number, ? extends List> map_generic = null; Map map_number_list = null; Map map_integer_arrayList = null; Map map_integer_col = null; Map super Number, ? super List> map_generic_super = null; map_generic = map_number_list; map_generic = map_integer_arrayList; // map_generic = map_integer_col; // compile error // map_generic = map_generic_super; // compile error // map_generic_super = map_generic; // compile error Map super List, ? super List> map_generic_list_super = null; Map map_generic_col_super = null; map_generic_list_super = map_generic_col_super; // map_generic_col_super = map_generic_list_super; // compile error
下面是摘錄自知乎的見解:
https://www.zhihu.com/questio...
PECS原則
最后看一下什么是PECS(Producer Extends Consumer Super)原則,已經(jīng)很好理解了:
1.頻繁往外讀取內(nèi)容的,適合用上界Extends。
2.經(jīng)常往里插入的,適合用下界Super。
? 如果要從集合中讀取類型T的數(shù)據(jù),并且不能寫入,可以使用 ? extends 通配符;(Producer Extends)
? 如果要從集合中寫入類型T的數(shù)據(jù),并且不需要讀取,可以使用 ? super 通配符;(Consumer Super)
? 如果既要存又要取,那么就不要使用任何通配符。
什么是上界?
下面代碼就是“上界通配符(Upper Bounds Wildcards)”:
Plate<? extends Fruit>
翻譯成人話就是:一個(gè)能放水果以及一切是水果派生類的盤子。再直白點(diǎn)就是:啥水果都能放的盤子。
Plate<? extends Fruit>是Plate
在這個(gè)體系中,上界通配符 “Plate<? extends Fruit>” 覆蓋下圖中藍(lán)色的區(qū)域。
什么是下界?
相對(duì)應(yīng)的,“下界通配符(Lower Bounds Wildcards)”:
Plate<? super Fruit>
表達(dá)的就是相反的概念:一個(gè)能放水果以及一切是水果基類的盤子。Plate<? super Fruit>是Plate
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/70773.html
摘要:引用泛型除了方法因不能使用外部實(shí)例參數(shù)外,其他繼承實(shí)現(xiàn)成員變量,成員方法,方法返回值等都可使用。因此,生成的字節(jié)碼僅包含普通的類,接口和方法。 為什么要使用泛型程序設(shè)計(jì)? 一般的類和方法,只能使用具體的類型:要么是基本類型,要么是自定義類的對(duì)應(yīng)類型;如果要編寫可以應(yīng)用于多種類型的代碼,這種刻板的限制對(duì)代碼的束縛就會(huì)很大。----摘自原書Ordinary classes and meth...
摘要:知識(shí)點(diǎn)總結(jié)泛型知識(shí)點(diǎn)總結(jié)泛型泛型泛型就是參數(shù)化類型適用于多種數(shù)據(jù)類型執(zhí)行相同的代碼泛型中的類型在使用時(shí)指定泛型歸根到底就是模版優(yōu)點(diǎn)使用泛型時(shí),在實(shí)際使用之前類型就已經(jīng)確定了,不需要強(qiáng)制類型轉(zhuǎn)換。 Java知識(shí)點(diǎn)總結(jié)(Java泛型) @(Java知識(shí)點(diǎn)總結(jié))[Java, Java泛型] [toc] 泛型 泛型就是參數(shù)化類型 適用于多種數(shù)據(jù)類型執(zhí)行相同的代碼 泛型中的類型在使用時(shí)指定 泛...
摘要:靜態(tài)變量是被泛型類的所有實(shí)例所共享的。所以引用能完成泛型類型的檢查。對(duì)于這個(gè)類型系統(tǒng),有如下的一些規(guī)則相同類型參數(shù)的泛型類的關(guān)系取決于泛型類自身的繼承體系結(jié)構(gòu)。事實(shí)上,泛型類擴(kuò)展都不合法。 前言 和C++以模板來實(shí)現(xiàn)靜多態(tài)不同,Java基于運(yùn)行時(shí)支持選擇了泛型,兩者的實(shí)現(xiàn)原理大相庭徑。C++可以支持基本類型作為模板參數(shù),Java卻只能接受類作為泛型參數(shù);Java可以在泛型類的方法中取得...
摘要:泛型類在類的申明時(shí)指定參數(shù),即構(gòu)成了泛型類。換句話說,泛型類可以看成普通類的工廠。的作用就是指明泛型的具體類型,而類型的變量,可以用來創(chuàng)建泛型類的對(duì)象。只有聲明了的方法才是泛型方法,泛型類中的使用了泛型的成員方法并不是泛型方法。 什么是泛型? 泛型是JDK 1.5的一項(xiàng)新特性,它的本質(zhì)是參數(shù)化類型(Parameterized Type)的應(yīng)用,也就是說所操作的數(shù)據(jù)類型被指定為一個(gè)參數(shù),...
摘要:可以看到,如果我們給泛型類制定了上限,泛型擦除之后就會(huì)被替換成類型的上限。相應(yīng)的,泛型類中定義的方法的類型也是如此。參考語言類型擦除下界通配符和的區(qū)別 本篇博客主要介紹了Java類型擦除的定義,詳細(xì)的介紹了類型擦除在Java中所出現(xiàn)的場(chǎng)景。 1. 什么是類型擦除 為了讓你們快速的對(duì)類型擦除有一個(gè)印象,首先舉一個(gè)很簡(jiǎn)單也很經(jīng)典的例子。 // 指定泛型為String List list1 ...
閱讀 3616·2021-11-23 09:51
閱讀 1493·2021-11-04 16:08
閱讀 3561·2021-09-02 09:54
閱讀 3626·2019-08-30 15:55
閱讀 2607·2019-08-30 15:54
閱讀 967·2019-08-29 16:30
閱讀 2057·2019-08-29 16:15
閱讀 2328·2019-08-29 14:05