首頁 Java java教程 Java泛型詳解

Java泛型詳解

Dec 19, 2016 pm 02:58 PM
泛型

  1. 概述
    在引入範型之前,Java類型分為原始類型、複雜類型,其中複雜類型分為數組和類別。引入範型後,一個複雜類型
    就可以在細分成更多的類型。
    例如原先的類型List,現在在細分成List, List等更多的類型。
    注意,現在List, List是兩種不同的類型,
    他們之間沒有繼承關係,即使String繼承了Object。下面的程式碼是非法的
        List ls = new ArrayList();
        List lo = ls;
    這樣設計的原因在於,根據lo的聲明,編譯器允許你在lo中加入任意物件(例如Integer),但是此物件是
    List,破壞了資料類型的完整性。
    在引入範型之前,要在類別中的方法支援多個資料類型,就需要對方法進行重載,在引入範型後,可以解決此問題
    (多態),更進一步可以定義多個參數以及傳回值之間的關係。
    例如
    public void write(Integer i, Integer[] ia);
    public void write(Double  d, Double[] da);
    的範式版本為
    public void write(T t, T[] ta);

    2.定義&使用
     類型參數的命名風格為:
     推薦你用簡練的名字作為形式類型參數的名字(如果可能,單個字符)。最好避免小寫字母,這使它和其他的普通
     的形式參數很容易被區分開來。
     使用T代表類型,無論何時都沒有比這更具體的類型來區分它。這常見於泛型方法。如果有多個類型參數,我們
     可能使用字母表中T的臨近的字母,例如S。
     如果一個泛型函數在一個泛型類別裡面出現,最好避免在方法的型別參數和類別的型別參數中使用相同的名字來避免混
     。對內部類別也是同樣。
     
     2.1 定義帶有類型參數的類別
     在定義帶有類型參數的類別時,在緊接類別後的內,指定一個或多個類型參數的名字,同時也可以對類型參數的取
     值範圍進行限定,多個類型參數之間以,號分隔。
     定義完型別參數後,可以在定義位置之後的類別的幾乎任意地方(靜態區塊,靜態屬性,靜態方法除外)使用型別參數,
     就像使用普通的型別一樣。
     注意,父類別定義的型別參數不能被子類別繼承。
     public class TestClassDefine {
         ....  
     }
     
     2.2 定義待型別參數方法
     在定義帶類型參數的方法範圍時,如同可見光的修飾>內,指定一個或多個型別參數的名字,
     同時也可以對型別參數的取值範圍進行限定,多個型別參數之間以,號分隔。
     定義完型別參數後,可以在定義位置之後的方法的任意地方使用型別參數,就像使用普通的型別一樣。
     例如:
     public T testGenericMethodDefine(T t, S s){
         ...
     }
     注意:定義帶類型參數的方法,定義主要目的是為了多個參數以及返回值}
     注意:定義帶類型參數的方法,定義主要目的是為了多個參數以及返回值之間的關係。例如本範例中T和S的繼
     承關係, 傳回值的型別和第一個型別參數的值相同。
     如果只是想實現多態,請優先使用通配符解決。通配符的內容請見下面章節。
     public void testGenericMethodDefine2(List s){
         ...
     }
     應改為
      參數賦值
     當對類別或方法的型別參數進行賦值時,要求對所有的型別參數進行賦值。否則,將得到一個編譯錯誤。
     
     3.1 對帶有類型參數的類別進行類型參數賦值
     對帶類型參數的類別進行類型參數賦值有兩種方式
     第一聲明類別變數或實例化時。例如
     List list;
     list = new ArrayList;
     第二繼承類別或實作介面時。例如
     public class MyList extends ArrayList implements List {...} 
     
     3.2 對帶型別參數方法進行賦值
     當呼叫範式方法時,編譯器會自動對型別參數進行賦值,當不能成功賦值時報編譯錯誤。例如
    public T testGenericMethodDefine3(T t, List list){
         ...
     }
     public T testGenericMethodDefine4(List list1, List list2){ 
     
     Number n = null;
     Integer i = null;
     Object o = null;
     testGenericMethodDefine(n, i);//此時為Number, SInteger Object, S為Integer
     
     List list1 = null;
     testGenericMethodDefine3(i, list1)//此時T為Number
     
     List list2 = nulltest;
     
     List list2 = nulltest;24Gene

     
     3.3 通配符
     在上面兩小節中,對是類型參數賦予特定的值,除此,還可以對型別參數賦予不確定值。例如
     List> unknownList;
     List extends Number> unknownNumberList;
     List super Integer> unknownBaseLineIntgerList; 
     注意: 在其中,對於類別參數值是未知數元素,不能像其中添加元素,
     因為,其類型是未知,所以編譯器無法識別添加元素的類型和容器的類型是否相容,唯一的例外是NULL

     List listString;
     List> unknownList2 = listString;
     unknownList = unknownList2;
     listString = unknownList;//編譯錯誤
     
    4. 陣列範式
     可以使用具有對類型參數值的類別聲明。 iListArray;
     new ArrayList[10];//編譯時錯誤
     
    5. 實作原理

    5.1. Java範式時編譯時技術,執行時不包含範式訊息,僅Class的實例包含了類型參數的定義資訊。
    泛型是透過java編譯器的稱為擦除(erasure)的前端處理來實現的。你可以(基本上就是)把它認為是一個從來源
    碼到原始碼的轉換,它把泛型版本轉換成非泛型版本。
    基本上,擦除去掉了所有的泛型類型資訊。所有在尖括號之間的類型資訊都被扔掉了,因此,比如說一個
    List類型被轉換為List。所有對類型變數的參考被替換成類型變數的上限(通常是Object)。而且,
    無論何時結果代碼類型不正確,都會插入一個到適當類型的轉換。
           T badCast(T t, Object o) {
             return (T) o; // unchecked warning
        這意味著它們不會添加任何的時間或空間上的負擔,這很好。不幸的是,這也意味著
    著你不能依靠他們來進行類型轉換。

    5.2.一個泛型類別被其所有呼叫共享
    下面的程式碼列印的結果是什麼?
           List l1 = new ArrayList();
           List l2 = new ArrayList(? ;
    或許你會說false,但你想錯了。它會列印出true。因為一個泛型類別的所有實例在運行時具有相同的運行時類別(class),
    而不管他們的實際類型參數。
    事實上,泛型之所以叫泛型,就是因為它對所有其可能的型別參數,有同樣的行為;同樣的類可以被當作許多不同
    的型別。作為一個結果,類別的靜態變數和方法也在所有的實例間共享。這就是為什麼在靜態方法或靜態初始化程式碼
    中或在靜態變數的宣告和初始化時使用型別參數(型別參數是屬於特定實例的)是不合法的原因。

    5.3. 轉型和instanceof
    泛型類別被所有其實例(instances)共享的另一個暗示是檢查一個實例是不是一個特定類型的泛型類別是沒有意義的。
           Collection cs = new ArrayList();
           if (cs instanceof Collection) { ...} // 非法
    =類似的,以下的類型轉換
    Collection ) cs;
    得到一個unchecked warning,因為運行時環境不會為你做這樣的檢查。

    6. Class的範式處理
    Java 5之後,Class變成範式化了。
    JDK1.5中一個變化是類別 java.lang.Class是泛型化的。這是把泛型擴展到容器類別之外的一個很有趣的例子。
    現在,Class有一個型別參數T, 你很可能會問,T 代表什麼?它代表Class物件代表的類型。比如說,
    String.class類型代表 Class,Serializable.class代表 Class
    這可以用來提高你的反射程式碼的型別安全。
    特別的,因為 Class的 newInstance() 方法現在返回一個T, 你可以在使用反射創建物件時得到更精確的類型。
    比如說,假定你要寫一個工具方法來進行一個資料庫查詢,給定一個SQL語句,並傳回一個資料庫中符合查詢條件
    的物件集合(collection)。
    一個方法是明確的傳遞一個工廠對象,像是下面的程式碼:
    interface Factory {
          public T[] make();
    }
    public Collection select(Factory factory, String statement) { 
           Collection result = new ArrayList();
           /* run sql query using jdbc.//
    . over jdbc results */
                T item = factory.make();
                    result.add( item );
           }
           return result;
    }
    你可以這樣呼叫:
    select(new Factory(){ 
        public EmpInfo make() { 
            return new EmpInfo();)
    也可以宣告一個類別EmpInfoFactory 來支援介面Factory:
    class EmpInfoFactory implements Factory { ...
        public EmpInfo make() { return new EmpInfo();}
    }
    接著呼叫:
    select(getMyEmpInfoselectiony(), "「string」解決方案
    se);缺點是它需要下面的二者之一:
    調用處那冗長的匿名工廠類,或為每個要使用的類型聲明一個工廠類並傳遞其對象給調用的地方
    這很不自然。
    使用class類型參數值是非常自然的,它可以被反射使用。沒有泛型的程式碼可能是:
    Collection emps = sqlUtility.select(EmpInfo.class, ”select * from emps”); ...
    public static Collection select(Class c, String sqlStatement) { 
        Collection List ();
        /* run sql query using jdbc */
        for ( /* iterate over jdbc results */ ) { 
       . and set all of item's fields from sql results */
            result.add(item);
        }
            return result;
    }
    但是這不能給我們回一個我們想要的精確類型的集合。現在Class是泛型的,我們可以寫:
    Collection emps=sqlUtility.select(EmpInfo.class, ”select * from emps”); ...
    public static Collection select(Class c, String sqlStatement) { 
        Collection result = new ArrayList();
        /* run sql query using jdbc /
       /* run sql query using jdbc /
      db   T item = c.newInstance();
            /* use reflection and set all of item's fields from sql results */
           result.addion (item);
    來透過一種類型安全的方式得到我們要的集合。
    這項技術是一個非常有用的技巧,它已成為一個在處理註釋(annotations)的新API中被廣泛使用的習慣用法。

    7. 新舊程式碼相容

    7.1. 為了保證程式碼的相容性,下面的程式碼編譯器(javac)允許,型別安全有你自己保證
    List l = new ArrayList();
    List l = new ArrayList();

    7.2. 在將你的類別庫升級為範式版本時,慎用協變式傳回值。
    例如,將程式碼
    public class Foo { 
        public Foo create(){
            return new Foo();
        }
    }
    public Foo create(){
            return new Bar();
        } 
    }
    採用協變性回傳值風格,將Bar   return new Bar() ;
        } 
    }
    要小心你類別庫的客戶端。


    更多Java泛型詳解相關文章請關注PHP中文網!


本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它們
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

泛型函數在Golang中解決可變參數類型的問題嗎? 泛型函數在Golang中解決可變參數類型的問題嗎? Apr 16, 2024 pm 06:12 PM

Go中的泛型函數解決了可變參數類型的問題:泛型函數允許使用類型參數,在運行時指定。這使得編寫可以處理不同類型參數的函數成為可能。例如,Max函數是一個泛型函數,它接受兩個可比較參數並傳回較大值。透過使用泛型函數,我們可以編寫更靈活通用的程式碼,可處理不同類型的參數。

golang中泛型的具體應用場景 golang中泛型的具體應用場景 May 04, 2024 am 11:45 AM

泛型在Go中的應用場景:集合運算:建立適用於任何類型的集合運算,例如篩選。資料結構:編寫通用的資料結構,如佇列,堆疊和映射,可儲存和操作各種類型的資料。演算法:編寫通用的演算法,如排序,搜尋和歸約,可處理不同類型的資料。

Java 函數泛型的上限和下限是什麼?如何使用? Java 函數泛型的上限和下限是什麼?如何使用? Apr 26, 2024 am 11:45 AM

Java函數泛型允許設定上限和下限。上限(extends)指定函數接受或傳回的資料類型必須是指定類型的子類型,例如。下限(super)指定函數接受或傳回的資料類型必須是指定類型的超類型,例如。泛型使用可提高程式碼的可重複使用性和安全性。

探討Golang中泛型的優勢與用途 探討Golang中泛型的優勢與用途 Apr 03, 2024 pm 02:03 PM

答案:Golang泛型是提高程式碼可重複使用性、靈活性、型別安全性和可擴充性的強大工具。詳細描述:優勢:程式碼可重複使用性:通用演算法和資料結構靈活性:執行階段建立特定類型實例類型安全性:編譯時類型檢查可擴展性:易於擴展和自訂用途:通用函數:排序、比較等通用資料結構:列表、映射、堆疊等類型別名:簡化類型聲明約束泛型:確保類型安全性

Java 泛型在 Android 開發的應用 Java 泛型在 Android 開發的應用 Apr 12, 2024 pm 01:54 PM

泛型在Android開發中的應用加強了程式碼的可重複使用性、安全性和靈活性。其語法包括宣告一個類型變數T,可用於操作類型參數化的資料。泛型實戰案例包括自訂資料適配器,允許適配器適應任何類型的自訂資料物件。 Android還提供了泛型清單類別(如ArrayList)和泛型方法,允許操作不同類型的參數。使用泛型的好處包括程式碼可重複使用性、安全性和靈活性,但需要注意指定正確的界限並適度使用,以確保程式碼的可讀性。

Golang泛型對函數簽章和參數的影響是什麼? Golang泛型對函數簽章和參數的影響是什麼? Apr 17, 2024 am 08:39 AM

泛型對Go函數簽章和參數的影響包括:型別參數:函數簽章可包含型別參數,指定函數可使用的型別。類型約束:類型參數可具有約束,指定其必須滿足的條件。參數類型推斷:編譯器可推斷未指定型別參數的型別。指定類型:可明確指定參數類型以呼叫泛型函數。這提高了程式碼的可重複使用性和靈活性,允許編寫可與多種類型一起使用的函數和類型。

泛型函數在Golang中的限制是什麼? 泛型函數在Golang中的限制是什麼? Apr 16, 2024 pm 05:12 PM

Go泛型函數的限制:僅支援型別參數,不支援值參數。不支援函數遞歸。不能明確指定型別參數,由編譯器推斷。

Java 枚舉類型如何與泛型搭配使用? Java 枚舉類型如何與泛型搭配使用? May 04, 2024 am 08:36 AM

Java中枚舉型別與泛型的結合:宣告帶有泛型的枚舉時需加上尖括號,T為型別參數。建立泛型類別時,同樣需新增尖括號,T為可儲存任何類型的類型參數。此結合可提高程式碼靈活性、類型安全性,並簡化程式碼。

See all articles