首頁 > Java > java教程 > 主體

Java--泛型

巴扎黑
發布: 2017-06-27 09:13:57
原創
1869 人瀏覽過

 

類型參數

 

在定義泛型類別或宣告泛型類別的變數時,使用尖括號來指定形式類型參數。形式類型參數與實際類型參數之間的關係類似於形式方法參數與實際方法參數之間的關係,只是類型參數表示類型,而不是表示值。

命名類型參數

建議的命名約定是使用大寫的單一字母名稱作為類型參數。這與 C++ 約定有所不同(請參閱 附錄 A:與 C++ 範本的比較),並反映了大多數泛型類別將具有少量類型參數的假定。對於常見的泛型模式,建議的名稱是:

K —— 鍵,例如映射的鍵。 
V —— 值,例如 List 和 Set 的內容,或 Map 中的值。 
E —— 異常類別。 
T —— 泛型

方法签名由方法名称和一个参数列表(方法的参数的顺序和类型)组成。
登入後複製

 

1.為什麼要用泛型

     # 1.放進去的元素沒有限制,放進兩種不同的對象,可能會造成異常。

      2.把物件丟進集合,集合遺失了物件的狀態訊息,集合只知道它盛裝的是Object、因此取出集合元素後通常還要強制轉換

2.什麼是泛型

##    ##Java的參數化類型稱為泛型,允許程式在建立集合的時候指定集合元素的類型

3.泛型的菱形語法

#後面只需要帶一對菱形括號,不需要再帶泛型

4.建立帶有泛型宣告的自訂類,為該類別定義建構器時,建構器名稱還是原來的類別名,不要增加泛型宣告。

5.從泛型類別派生子類,繼承的時候必須為父類傳入實際的參數

#public class A extends Apple{}

#裡面所有重寫父類別的方法變成對應的類型

也可以不傳入實際的參數

public class A extends Apple{}

當成Obejct類型處理

#6.並不存在泛型類別

  不管泛型的實際型別參數是什麼,他們在執行時總有相同的類別。不管泛型的類型形參傳入哪一種類型實參,(在於Java中的泛型這一概念提出的目的,導致其只是作用於程式碼編譯階段,在編譯過程中,對於正確檢驗泛型結果後,會將泛型的相關資訊擦出,也就是說,成功編譯過後的class檔案中是不包含任何泛型資訊的。 #。形參

           

###############################################

靜態變數是被泛型類別所有實例所共享的。對於宣告為MyClass的類,存取其中的靜態變數的方法仍然是MyClass.myStaticVar。不管是透過new MyClass還是new MyClass建立的對象,都是共用一個靜態變數。假設允許型別參數作為靜態變數的型別。所以考慮以下情況:

 

 

MyClass class1 = new MyClass();

 

MyClass class2 = new MyClass();#class1.myStaticVar = "hello";

class2.myStaticVar = 5; 

##由於泛型系統的型別擦除(type erasure)。 myStaticVar被還原成Object類型,然後當呼叫class1.myStaticVar= "hello"; 編譯器進行強制型別轉換,即myStaticVar = (String)"hello";接著呼叫class2.myStaticVar語句時,編譯器繼續進行強制型別轉換,myStaticVar = (Integer)Integer.valueOf(5); 此時myStaticVar是String類型的,當然語句會在執行時拋出ClassCastException異常,這樣一來就存在型別安全問題。因此泛型系統不允許類別的靜態變數以類型參數作為變數類型。

   

 

系統中不會真正產生泛型類,因此instanceod運算子後不能使用泛型類,因為根本不存在!

7.類型萬用字元

 注意的是,如果Foo是Bar的一個子類型,而G是具有泛型聲明的類別或接口,G並不是G的子類型。

假設List在邏輯上可以視為List的父類,那麼a.test(list)將不會有錯誤提示了,那麼問題就出來了,透過getData()方法取出資料時到底是什麼類型呢? Integer? Float? 還是Object?且由於在程式設計過程中的順序不可控性,導致在必要的時候必須進行類型判斷,且進行強制類型轉換。顯然,這與泛型的理念矛盾,因此,

在邏輯上

List###不能視為###List###的父類別####### ##############################為了表示各種泛型集合的父類,可以使用型別通配符,型別通配符是一個問號,他可以匹配任何類型:#########類型通配符一般是使用? 代替具體的類型實參。注意了,這裡是類型實參,而不是類型形參!且List在邏輯上是List、List...等所有List<具體型別實參>的父類別。由此,我們依然可以定義泛型方法,來完成此類需求。 ######### #########不管list的真實型別是什麼,包含的都是Object###########注意:帶通配符的僅僅表示它是各種泛型集合的父類,並不能把元素加入其中############ ######### ###

並不知道c集合中元素的類型,不能在其中新增物件

7.1 設定通配符上限

## 

因為List 不是List 的子類型,所以編譯出錯這回收可以以被限制的泛型通配符

#7.2 設定類型形參的上限

8.泛型方法

定義類別、介面時沒有使用類型形參,定義方法時想自己定義

#獨立的泛型靜態方法,在不考慮多執行緒的情況下,同一時間點,只會被初始化並調用一次,不會出現重疊初始化並錯誤調用,不會出現類似資料庫中讀髒資料的情況,所以不會出現強制類型轉換的程式碼錯誤。

#方法宣告中定義的形參只能在該方法中使用。 與類別、介面不同,方法中的泛型無需顯示傳入實際型別參數

一定不能讓編譯器迷惑你傳入的到底是什麼型別

#例如test a, Collection b>你傳進去一個String類型的,一個Object類型的,編譯器不知道你這T到底是啥類型的

可以改成 a, Collection b>

#泛型方法

##(在型別參數一節中)您已經看到,透過在類別的定義中新增一個形式類型參數列表,可以將類別泛型化。方法也可以被泛型化,不管它們定義在其中的類別是不是泛型化的。

泛型類別在多個方法簽章間實作型別約束。在 List 中,型別參數 V 出現在 get()、add()、contains() 等方法的簽章中。當建立一個 Map 類型的變數時,您就在方法之間宣稱一個型別約束。您傳遞給 add() 的值將與 get() 傳回的值的類型相同。
類似地,之所以宣告泛型方法,一般是因為您想要在該方法的多個參數之間宣稱一個型別約束。例如,下面程式碼中的ifThenElse() 方法,根據它的第一個參數的布林值,它將傳回第二個或第三個參數:
public T ifThenElse(boolean b, T first, T second) {

return b ? first : second;

#}

注意,您可以呼叫ifThenElse(),而不用明確地告訴編譯器,您想要T 的什麼值。編譯器不必明確地被告知 T 將具有什麼值;它只知道這些值都必須相同。編譯器允許您呼叫下面的程式碼,因為編譯器可以使用類型推理來推斷出,替代T 的String 滿足所有的類型約束:

#########String s = ifThenElse(b, "a", "b");###################類似地,您可以呼叫:#########

Integer i = ifThenElse(b, new Integer(1), new Integer(2));

但是,編譯器不允許下面的程式碼,因為沒有型別會滿足所需的型別限制:

String s = ifThenElse(b, "pi", new Float( 3.14));

為什麼您選擇使用泛型方法,而不是將類型T 加到類別定義呢? (至少)有兩種情況應該這樣做:

當泛型方法是靜態的時,這種情況下不能使用類別類型參數。

當T 上的型別約束對於方法真正是局部的時,這表示沒有在相同類別的另一個方法簽章中使用相同型別T 的約束。透過使得泛型方法的型別參數對於方法是局部的,可以簡化封閉型別的簽章。


有限制型別

#在前一螢幕 泛型方法的例子中,類型參數V 是無約束的或無限制的類型。有時在尚未完全指定類型參數時,需要對類型參數指定附加的約束。

考慮範例Matrix 類,它使用型別參數V,由Number 類別來限制:

public class Matrix { ... }

編譯器可讓您建立Matrix 或Matrix類型的變量,但是如果您試圖定義Matrix 類型的變量,則會出現錯誤。類型參數 V 被判斷為由 Number 限制 。在沒有型別限制時,假設型別參數由 Object 限制。這就是為什麼前一螢幕 泛型方法 中的例子,允許 List.get() 在 List 上呼叫時傳回 Object,即使編譯器不知道型別參數 V 的型別。

9.泛型方法和型別通配符的差異

如果某個方法中的一個形參(a)的類型或傳回值的類型依賴另一個形參(b)的類型,則形參(b)的類型聲明不應該使用通配符,只能考慮使用在方法簽章中宣告類型形參,也就是泛型方法。

我理解的是類型通配符是不需要添加、修改集合裡面的元素,並且是依附於別人而不是別人依附於他,使用

類型通配符既可以在方法簽章定義形參的類型,也可以用來定義變數的類型,泛型方法中的類型形參必須在對應方法中顯示宣告。

10.擦拭與轉換

#當把一個具有泛型訊息的物件賦給另當一個沒有泛型資訊的變數時,所有在尖括號之間的類型資訊都會被丟掉。

#將li賦給List的時候,編譯器會擦拭掉前者的泛型訊息,即遺失掉list集合裡元素的類型資訊。

Java又允許直接把list物件賦給一個List的變量,所以程式可以編譯通過,只是發出「未經檢查的轉換」(邏輯上的父類別直接賦給子類別),但對list變數實際上引用的是list集合,所以當試圖把該集合裡的元素當成String類型的物件取出時,將會引發執行時異常

11.泛型與陣列

#

java泛型有一個很重要的設計原則---如果一段程式碼在編譯時沒有提出「未經轉換的異常」警告,程式不會造成ClassCastException異常,基於這個原因,所有陣列元素的型別不能包含型別變數或型別形參,除非是無上限的型別通配符,但可以宣告元素型別包含型別變數或型別形參的陣列

假設能通過,不會造成任何警告,但會引發例外

##改變成如下格式:

第一行會有「未經檢查的轉換」警告,最後一行也會引發例外

建立無上限的通配符泛型數組

編譯不會發出任何警告,但是運行時候會引發異常,因為程式需要將lsa的第一個數組元素的第一個集合元素強制轉換成String類型,所以程式應該透過instanceof運算子來保證它的資料類型

#於此類似,創建元素類型是類型變數的陣列物件也會導致編譯錯誤

T[] makeArray(Collection coll)

{ return new T[cool.size()]

}

#類型變數在執行時不存在,編譯器無法確定實際類型是什麼

 

以上是Java--泛型的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板