摘要:好了,有了這樣的背景知識,我們可以來看一下上界通配了,在中,可以使用來界定一個上界,的意思是所有屬于的子類,是上界,不能突破天界啊,我們具體化一下,的意思就是,所有的子類都可以匹配這個通配符。
1、上界通配符
首先,需要知道的是,Java語言中的數組是支付協變的,什么意思呢?看下面的代碼:
static class A extends Base{ void f() { System.out.println("A.f"); } } static class B extends A { void f() { System.out.println("B.f"); } } static class C extends B { void f() { System.out.println("C.f"); } } static { A[] arrayA = new A[3]; arrayA[0] = new A(); arrayA[1] = new B(); arrayA[2] = new C(); for (A item : arrayA) { item.f(); } } //output A.f B.f C.f
我們明明讓數組的類型為A,但是向其中加入B、C也是可以行得通的,為什么呢?我們發現B繼承了A,屬于A的子類,C繼承了B,屬于B的子類,Java中的繼承是可以傳遞的,所以C依然屬于A的子類,所以B和C都是A的子類,另外一點,在Java中,類型向上轉換是非常自然的,不需要強制轉換會自動進行,也就是說,B和C的實例都可以自動轉換為類型A的實例。好了,有了這樣的背景知識,我們可以來看一下上界通配了,在java中,可以使用 extends Type> 來界定一個上界, extends Type>的意思是所有屬于Type的子類,Type是上界,不能突破天界啊,我們具體化一下, extends A>的意思就是,所有A的子類都可以匹配這個通配符。所有我們的B、C的實例以及他們的子類的實例都可以匹配,但是Base就不可以,因為Base是A的父類,而我們的上界是A啊,所以當然不能是Base了。很自然的,我們有下面的代碼:
A a = new B(); A b = new C(); C c = new C(); List extends A> list = new ArrayList(); list.add(a); list.add(b); list.add(c);
我們覺得很自然這樣做是無可厚非的,對吧?但是編譯器很顯然不允許我們這樣做,為什么?我們的list的類型使用了上界通配符啊,而且匹配的是所有A的子類,而我們add的都是A的子類啊,為什么不可以呢?我們再來看一下 extends A>,我們的list可以持有任何A的子類對象,也就是A、B、C的實例都是可以的,那我們是不是可以把 extend B>認為是 extends A>的子類呢? entends C>呢?我們暫且認為是可以這樣吧,那看下面的這個方法:
void joke(List extends A> list) { A a = new B(); A b = new C(); C c = new C(); list.add(a); list.add(b); list.add(c); }
當然上面的代碼是無法通過編譯的,我們分析一下為什么,記住 extends A>是設定上界,所以,joke方法的參數是開放的,我們可以傳進去一個 extend A>的list,也可以是一個 extends B>的list,還可以是一個 extends C>的list。因為下面的代碼是可以通過編譯的:
private static void jokeIn(List list) { // } static { List extends A> list = new ArrayList<>(); List extends B> list1 = new ArrayList<>(); List extends C> list2 = new ArrayList<>(); jokeIn(list); jokeIn(list1); jokeIn(list2); }
好吧,問題來了,當我們傳到joke方法中的參數是List extends A>的時候,方法內部的add都是可以接受的,但是當我們傳進去的參數是List extends B>的時候,list.add(a)明顯是無法成功的,因為我們的list將可以允許持有B的子類,但是A不在這個范圍里面,所以是不合法的,當傳進去的是List extends C>的時候呢?連list.add(B)也不允許了。所以這就是問題所在,所以不允許這樣的代碼通過編譯是明智的,因為我們不能總是保證調用joke方法的用戶會嚴格傳進來一個List extends A>的參數。
那怎么使用上界呢?換句話說,如何來產生一個List extends A>的list呢?還記得一開始我們說的數組協變嗎?下面的代碼使用了java語言數組具有協變能力來產生一個具有上界的list:
List extends A> list = Arrays.asList(a, b);
Arrays.asList(T ... data)使用了ArrayList的一個構造函數:
ArrayList(E[] array) { a = Objects.requireNonNull(array); }
可以看到使用了數組的協變,使得我們可以在Arrays.asList里面傳遞進去所以A的子類對象。
2、下界通配符
上界定義了可以達到了最高點,超出就是違法的;而下界則是說明了底線,你只能比底線更高級,低于底線就是違法的。在java里面,可以使用 super Type>來表達下界的意義,具體一點, super A>表達的和 extends A>是兩個相反的方向,前者是說所有基于A的基類,后者是說所有基于A的子類,我們再來看一下下面這個方法:
void joke(List super A> list) { A a = new B(); A b = new C(); C c = new C(); list.add(a); list.add(b); list.add(c); }
此時的joke方法的參數是List super A>,此時List super B>和List super C>都變成了List super A>的父類了,因為實際上List super B>和List super C>表達的能力比List super A>更強,也就是List super C>包含了List super B>和List super A>,而List super B>則包含了List super A>,好了,說明了這些之后,我們再來看一下對joke方法的調用會出現哪些情況:
static { List super A> list = new ArrayList<>(); List super B> list1 = new ArrayList<>(); List super C> list2 = new ArrayList<>(); jokeIn(list); jokeIn(list1); // error jokeIn(list2); //error }
好吧,問題出現了,我們可以將List super A> 的參數傳遞給joke,因為這正是我們需要的,而我們也知道List super A>的表達能力在List super B>和 super C>中是最低的,所以,當我們將一個表達能力強于List super A>的參數傳遞給joke之后,編譯器報錯了。當然,這僅僅是為了說明所謂下界的界定。
有了下界,我們可以使用下面的代碼來為我們工作了:
List super A> lists = new ArrayList<>(); lists.add(a); lists.add(b); lists.add(c);
解釋一下,lists里面的元素類型是這樣一種類型,這種類型是A的基類,我們只是界定了下界,只要高于這個下界,就可以被lists接收,而b、c的基類都是A,可以被lists接收,所以上面的代碼是可以工作的。
3、無界通配符
有了上界和下界,還有無界,需要說明的一點是,不能同時使用上界和下界,因為有無界啊(開玩笑的)!!
我們在java中使用>來表達無界,對于>,目前來講鎖表達的意思是:
我是想要java的范型來編寫這段代碼,我在這里并不是想使用原生類 型,但是在當前這種情況下,泛型參數可以持有任何類型。 ----來自《java編程思想》15.10.3 無界通配符(686頁)
使用無界通配符的一種場景是:如果向一個使用>的方法傳遞了一個原生類型,那么對編譯器來說可能會推斷出實際的參數類型,使得這個方法可以回轉并且調用另外一個使用這個確切類型的方法。這叫做“類型捕獲”,看下面的代碼:
static class Holder{ private T data; public Holder() { } public Holder(T data) { this.data =data; } public T getData() { return data; } public void setData(T data) { this.data = data; } } static void actual(Holder holder) { System.out.println(holder.getData()); } static void func(Holder> holder) { actual(holder); } static { Holder> holder = new Holder<>("hujian"); func(holder); }
可以看到,actual的參數是具體的T,而func的參數是無界的>,這里發生了一件參數類型捕獲的事情,在調用func的時候,類型被捕獲,而可以在actual方法中使用我們從func中傳遞進來的無界參數。
可以使用無界通配符來接收多個類型的對象,然后根據不同的類型來交付給不同的方法來處理,可以回憶一下操作系統的中斷處理程序的處理方法,通過安裝一些中斷類型和與之對應的handler,然后通過控制程序來將中斷處理信號分發到不同的handler中處理,其實思想是一樣的,可以看一下下面的代碼來理解這個模型:
staticvoid actual(Holder holder) { T data = holder.getData(); if (data instanceof String) { actual((String) data); } else if (data instanceof Integer) { actual((Integer) data); } else if (data instanceof Double) { actual((Double) data); } } static void actual(String holder) { System.out.print("string:" + holder); } static void actual(Integer holder) { System.out.println("Integer:" + holder); } static void actual(Double holder) { System.out.println("double:" + holder); } static void func(Holder> holder) { actual(holder); }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/70560.html
摘要:虛擬機中并沒有泛型類型對象,所有的對象都是普通類。其原因就是泛型的擦除。中數組是協變的,泛型是不可變的。在不指定泛型的情況下,泛型變量的類型為該方法中的幾種類型的同一個父類的最小級,直到。 引入泛型的主要目標有以下幾點: 類型安全 泛型的主要目標是提高 Java 程序的類型安全 編譯時期就可以檢查出因 Java 類型不正確導致的 ClassCastException 異常 符合越早出...
摘要:使用強轉的話,只能強轉成和它的基類,如果強轉成的子類的話,有可能會報運行時異常。擁有類型,它是的子類型因此,我們可以將賦給類型為的變量在聲明處設置后,就可以和或它的子類進行比較了。 歡迎關注我的博客:songjhhs blog原文連接:對比Java泛型中的extends/super和Kotlin的out/in 在 Java 泛型中,有一個叫做通配符上下界 bounded wildca...
摘要:泛型方法泛型類中可以定義靜態非靜態的泛型方法。上述泛型類會被替換成下面形式一般使用第一個限定類型替換變為原始類型,沒有限定類型,使用替換。 引言 在面向對象的世界里,我們如果需要一個容器來盛裝對象。舉個例子:一個籃子。我們可以用這個籃子裝蘋果,也可以用這個籃子裝香蕉。基于 OOP 的思想,我們不希望為蘋果和香蕉分別創建不同的籃子;同時,我們希望放進籃子里的是蘋果,拿出來的還是蘋果。于是...
摘要:代碼使用泛型類中不依賴于類型參數的方法。委托依賴于動態綁定,因為它要求給定的方法調用可以在運行時調用不同的代碼段。委托捕獲操作并將其發送給另一個對象。委托可以被看作是在對象層次上的復用機制,而繼承是類層次上的復用機制。 大綱 設計可復用的類 繼承和重寫 重載(Overloading) 參數多態和泛型編程 行為子類型與Liskov替換原則 組合與委托 設計可復用庫與框架 API和庫...
摘要:靜態變量是被泛型類的所有實例所共享的。所以引用能完成泛型類型的檢查。對于這個類型系統,有如下的一些規則相同類型參數的泛型類的關系取決于泛型類自身的繼承體系結構。事實上,泛型類擴展都不合法。 前言 和C++以模板來實現靜多態不同,Java基于運行時支持選擇了泛型,兩者的實現原理大相庭徑。C++可以支持基本類型作為模板參數,Java卻只能接受類作為泛型參數;Java可以在泛型類的方法中取得...
閱讀 3473·2021-11-18 10:02
閱讀 3720·2021-09-13 10:25
閱讀 1928·2021-07-26 23:38
閱讀 2574·2019-08-30 15:44
閱讀 2279·2019-08-30 13:51
閱讀 1232·2019-08-26 11:35
閱讀 2277·2019-08-26 10:29
閱讀 3450·2019-08-23 14:56