Java程式設計思想創薪理解——第15章,泛型

這是Java重大升級的結果,泛型實現了引數化型別的概念,使程式碼可以應用於多種型別。“泛型”這個術語的意思是:“適用於許多許多的型別”。

根據原書作者的經驗,理解了Java泛型的邊界所在,你才能成為程式高手。因為只有知道了某個技術不能做到什麼,你才能更好地做到所能做到的(部分原因是,不必浪費時間在死衚衕裡亂轉)。

有許多原因促成了泛型的出現,而最引人注目的一個原因,就是為了創造容器類。容器,就是存放要使用的物件的地方。

package com。exam。cn;class Gum { static { System。out。println(“Loading Gum”); }}public class Holder3 { private T a; public Holder3(T a) { this。a = a; } public void set(T a) { this。a = a; } public T get() { return a; } public static void main(String[] args) { Holder3 h3 = new Holder3(new Gum()); Gum a = h3。get(); }}

現在,當你建立Holder3物件時,必須指明想持有什麼型別的物件,將其置於尖括號內。就像main()中那樣。然後你就只能在Holder3中取出它持有的物件時,自動地就是正確的型別。

這就是Java泛型的核心概念:告訴編譯器想使用什麼型別,然後編譯器幫你處理一切細節。一般而言,你可以認為泛型與其他的型別差不多,只不過他們碰巧有型別引數罷了。稍後我們會看到,在使用泛型時,我們只需指定它們的名稱以及型別引數列表即可。

一個元組類庫,僅一次方法呼叫就能返回多個物件,你應該經常需要這種功能吧。可是return語句只允許返回單個物件,因此解決辦法就是建立一個物件,用它來持有想要返回的多個物件。當然,可以在每次需要的時候,專門建立一個類來完成這樣的工作。可是有了泛型,我們就能夠一次性的解決該問題,以後再也不用在這個問題上浪費時間了。同時,我們在編譯器就能確保型別安全。這個概念稱為元祖,他是將一組物件直接打包儲存於其中的一個單一物件。這個容器物件允許讀取其中元素,但是不允許向其中存放新的物件。(這個概念也稱為資料傳送物件。或信使)

通常元組可以具有任意長度,同時,元組中的物件可以是任意不同的型別。不過,我們希望能夠為每一個物件指明其型別,並且從容器中讀取出來時,能夠得到正確的型別。要處理不同長度的問題,我們需要建立多個不同元組。下面的程式是一個二維元組,它能夠持有兩個物件:

package com。exam。cn;import net。mindview。util。TwoTuple;public class TowCouple { public final A first; public final B second; public TowCouple(A first, B second) { this。first = first; this。second = second; } /* 2021年2月24日 * exam1。com。exam。cn * 請用一句話描述這個方法的作用: * */ @Override public String toString() { return “(”+first+“,”+second+“)”; } /**2021年2月24日 * @return the first */ public A getFirst() { return first; } /**2021年2月24日 * @return the second */ public B getSecond() { return second; }}

第一次閱讀上面的程式碼時,你也許會想,這不是違反了Java程式設計的安全性原則嗎?first和second應該宣告為private,然後提供getFirst()和getSecond()之類的訪問方法才對呀,讓我們仔細看看這個例子中的安全性,客戶端程式可以讀取first和second物件,然後可以隨時能所欲的使用這兩個物件。但是卻無法將其他值賦值給first和second。因為final宣告為你買了相同的安全保險,而且這種格式更簡潔明瞭。

我們可以利用繼承機制實現長度更長的元組,從下面的例子中可以看到,增加型別引數是件很簡單的事情。

package com。exam。cn;import net。mindview。util。TwoTuple;public class ThreeTuple extends TwoTuple { public final C third; public ThreeTuple(A a, B b, C third) { super(a, b); this。third = third; } @Override public String toString() { return “(” + first + “,” + second + “,” + third + “)”; } public static ThreeTuple f() { return new ThreeTuple(1, 2, “a”); } public static void main(String[] args) { ThreeTuple ttsi=f(); // ttsi。third=“abc”; }}

為了使用陣列,你只需定義一個長度適合的元組,將其作為方法的返回值,然後再return語句中建立該陣列,並返回即可。

透過ttsi。third=“abc”;語句地錯誤,我們可以看出,final聲明確實能夠保護public元素,在物件被構造出來之後,宣告為final地元素便不能被再賦予其他值了。

泛型介面:泛型也可以用於介面。例如生成器,這是一種專門負責建立物件的類。實際上,這是工廠方法設計模式的一種應用。不過,當使用生成器建立新的物件時,他不需要任何引數,而工廠方法一般需要引數。也就是說,生成器無需額外的資訊就知道如何建立新物件。一般而言,一個生成器之定義一個方法,該方法用以產生新的物件。在這裡,就是next()方法。

package com。exam。cn。generator;public interface Generator { T next();}

方法next()的返回型別是引數化T。正如你所見到的,介面使用泛型與類使用泛型沒什麼區別。

泛型方法:我們看到的泛型,都是應用於整個類上,但同樣可以在類中包含引數化方法,而這個方法所在的類可以是泛型類,也可以不是泛型類。也就是說,是否擁有泛型方法,與其所在的類是否是泛型沒有關係。

泛型方法使得該方法能夠獨立於類而產生變化。以下是一個基本的指導原則:無論何時,只要你能做到,你就應該儘量使用泛型方法。也就是說,如果使用泛型方法可以取代將整個類泛型化,那麼就應該只使用泛型方法,因為它可以使事情更清楚明白。另外,對於一個static的方法而言,無法訪問泛型類的型別引數,所以如果static方法需要使用泛型能力,就必須使其成為泛型方法。

要定義泛型方法,只需將泛型引數列表置於返回值之前,就像下面這樣:

package com。exam。cn。generator;public class GenericMethods { public void f(T x){ System。out。println(x。getClass()。getName()); } public static void main(String[] args) { GenericMethods gm=new GenericMethods(); gm。f(“”); gm。f(1); gm。f(1。0); gm。f(1。0F); gm。f(‘c’); gm。f(gm); }}

注意,當使用泛型類時,必須在建立物件的時候指定型別引數的值,而使用泛型方法的時候,通常不必指明引數型別,因為編譯器會為我們找出具體型別。這稱為型別引數推斷。因此,我們可以像呼叫普通方法一樣呼叫f(),而且就好像是f()被無限次地過載過。他甚至可以接受GenericMethods作為其引數型別。

如果呼叫f()時傳入基本型別,自動打包機制就會介入其中,將基本型別的值包裝為對應的物件。事實上,泛型方法與自動打包避免了許多以前我們不得不自己編寫出來的程式碼。

可變引數與泛型方法,泛型方法與可變引數列表能夠很好地共存:

package com。exam。cn。generator;import java。util。ArrayList;import java。util。List;public class GenericVarargs { public static List makeList(T。。。 args){ List result=new ArrayList(); for(T item:args){ result。add(item); } return result; } public static void main(String[] args) { List ls=makeList(“A”); System。out。println(ls); ls=makeList(“A”,“B”,“C”); System。out。println(ls); ls=makeList(“ABCDEFGHIJKLMNOPQRSTUVWXYZ”); System。out。println(ls); }}輸出結果:[A][A, B, C][ABCDEFGHIJKLMNOPQRSTUVWXYZ]

makeList()方法展示了與標準類庫中java。util。Arrays。asList()方法相同的功能。

萬用字元:List<? extends Fruit>,你可以將其讀作“具有任何從Fruit繼承的型別的列表”

更多精彩請點選

https://www。toutiao。com/i6932326235255112200/?group_id=6932326235255112200