java泛型方法,java泛型方法的定義和使用

本文目錄一覽:

java中泛型的使用

一般使用在集合上,比如現在將一個字元串類型放在集合裡面,這時候,放入集合的字元會失去其本身的類型,只能是object類型,比如想要對這這個值進行轉換, 很容易出現類型轉換錯誤。可以使用泛型解決這個問題。

請教關於java的泛型方法

Java泛型詳解

概述

在引入范型之前,Java類型分為原始類型、複雜類型,其中複雜類型分為數組和類。引入范型後,一個複雜類型

就可以在細分成更多的類型。

例如原先的類型List,現在在細分成ListObject, ListString等更多的類型。

注意,現在ListObject, ListString是兩種不同的類型,

他們之間沒有繼承關係,即使String繼承了Object。下面的代碼是非法的

    ListString ls = new ArrayListString();

    ListObject lo = ls;

這樣設計的原因在於,根據lo的聲明,編譯器允許你向lo中添加任意對象(例如Integer),但是此對象是

ListString,破壞了數據類型的完整性。

在引入范型之前,要在類中的方法支持多個數據類型,就需要對方法進行重載,在引入范型後,可以解決此問題

(多態),更進一步可以定義多個參數以及返回值之間的關係。

例如

public void write(Integer i, Integer[] ia);

public void write(Double  d, Double[] da);

的范型版本為

public T void write(T t, T[] ta);

2. 定義使用

 類型參數的命名風格為:

 推薦你用簡練的名字作為形式類型參數的名字(如果可能,單個字元)。最好避免小寫字母,這使它和其他的普通

 的形式參數很容易被區分開來。

 使用T代表類型,無論何時都沒有比這更具體的類型來區分它。這經常見於泛型方法。如果有多個類型參數,我們

 可能使用字母表中T的臨近的字母,比如S。

 如果一個泛型函數在一個泛型類裡面出現,最好避免在方法的類型參數和類的類型參數中使用同樣的名字來避免混

 淆。對內部類也是同樣。

 

 2.1 定義帶類型參數的類

 在定義帶類型參數的類時,在緊跟類命之後的內,指定一個或多個類型參數的名字,同時也可以對類型參數的取

 值範圍進行限定,多個類型參數之間用,號分隔。

 定義完類型參數後,可以在定義位置之後的類的幾乎任意地方(靜態塊,靜態屬性,靜態方法除外)使用類型參數,

 就像使用普通的類型一樣。

 注意,父類定義的類型參數不能被子類繼承。

 public class TestClassDefineT, S extends T {

     ….  

 }

 

 2.2 定義待類型參數方法

 在定義帶類型參數的方法時,在緊跟可見範圍修飾(例如public)之後的內,指定一個或多個類型參數的名字, 同時也可以對類型參數的取值範圍進行限定,多個類型參數之間用,號分隔。

 定義完類型參數後,可以在定義位置之後的方法的任意地方使用類型參數,就像使用普通的類型一樣。

 例如:

 public T, S extends T T testGenericMethodDefine(T t, S s){

     …

 }

 注意:定義帶類型參數的方法,騎主要目的是為了表達多個參數以及返回值之間的關係。例如本例子中T和S的繼 承關係, 返回值的類型和第一個類型參數的值相同。

 如果僅僅是想實現多態,請優先使用通配符解決。通配符的內容見下面章節。

 public T void testGenericMethodDefine2(ListT s){

     …

 }

 應改為

 public void testGenericMethodDefine2(List? s){

     …

 }

 

3. 類型參數賦值

 當對類或方法的類型參數進行賦值時,要求對所有的類型參數進行賦值。否則,將得到一個編譯錯誤。

 

 3.1 對帶類型參數的類進行類型參數賦值

 對帶類型參數的類進行類型參數賦值有兩種方式

 第一聲明類變數或者實例化時。例如

 ListString list;

 list = new ArrayListString;

 第二繼承類或者實現介面時。例如

 public class MyListE extends ArrayListE implements ListE {…} 

 

 3.2 對帶類型參數方法進行賦值

 當調用范型方法時,編譯器自動對類型參數進行賦值,當不能成功賦值時報編譯錯誤。例如

 public T T testGenericMethodDefine3(T t, ListT list){

     …

 }

 public T T testGenericMethodDefine4(ListT list1, ListT list2){

     …

 }

 

 Number n = null;

 Integer i = null;

 Object o = null;

 testGenericMethodDefine(n, i);//此時T為Number, S為Integer

 testGenericMethodDefine(o, i);//T為Object, S為Integer

 

 ListNumber list1 = null;

 testGenericMethodDefine3(i, list1)//此時T為Number

 

 ListInteger list2 = null;

 testGenericMethodDefine4(list1, list2)//編譯報錯

 

 3.3 通配符

 在上面兩小節中,對是類型參數賦予具體的值,除此,還可以對類型參數賦予不確定值。例如

 List? unknownList;

 List? extends Number unknownNumberList;

 List? super Integer unknownBaseLineIntgerList; 

 注意: 在Java集合框架中,對於參數值是未知類型的容器類,只能讀取其中元素,不能像其中添加元素, 因為,其類型是未知,所以編譯器無法識別添加元素的類型和容器的類型是否兼容,唯一的例外是NULL

 ListString listString;

 List? unknownList2 = listString;

 unknownList = unknownList2;

 listString = unknownList;//編譯錯誤

 

4. 數組范型

 可以使用帶范型參數值的類聲明數組,卻不可有創建數組

 ListInteger[] iListArray;

 new ArrayListInteger[10];//編譯時錯誤

 

5. 實現原理

5.1. Java范型時編譯時技術,在運行時不包含范型信息,僅僅Class的實例中包含了類型參數的定義信息。

泛型是通過java編譯器的稱為擦除(erasure)的前端處理來實現的。你可以(基本上就是)把它認為是一個從源碼到源碼的轉換,它把泛型版本轉換成非泛型版本。

基本上,擦除去掉了所有的泛型類型信息。所有在尖括弧之間的類型信息都被扔掉了,因此,比如說一個ListString類型被轉換為List。所有對類型變數的引用被替換成類型變數的上限(通常是Object)。而且,無論何時結果代碼類型不正確,會插入一個到合適類型的轉換。

       T T badCast(T t, Object o) {

         return (T) o; // unchecked warning

       }

類型參數在運行時並不存在。這意味著它們不會添加任何的時間或者空間上的負擔,這很好。不幸的是,這也意味著你不能依靠他們進行類型轉換。

5.2.一個泛型類被其所有調用共享

下面的代碼列印的結果是什麼?

       ListString l1 = new ArrayListString();

       ListInteger l2 = new ArrayListInteger();

       System.out.println(l1.getClass() == l2.getClass());

或許你會說false,但是你想錯了。它列印出true。因為一個泛型類的所有實例在運行時具有相同的運行時類(class),

而不管他們的實際類型參數。

事實上,泛型之所以叫泛型,就是因為它對所有其可能的類型參數,有同樣的行為;同樣的類可以被當作許多不同的類型。作為一個結果,類的靜態變數和方法也在所有的實例間共享。這就是為什麼在靜態方法或靜態初始化代碼中或者在靜態變數的聲明和初始化時使用類型參數(類型參數是屬於具體實例的)是不合法的原因。

5.3. 轉型和instanceof

泛型類被所有其實例(instances)共享的另一個暗示是檢查一個實例是不是一個特定類型的泛型類是沒有意義的。

       Collection cs = new ArrayListString();

       if (cs instanceof CollectionString) { …} // 非法

類似的,如下的類型轉換

CollectionString cstr = (CollectionString) cs;

得到一個unchecked warning,因為運行時環境不會為你作這樣的檢查。

6. Class的范型處理

Java 5之後,Class變成范型化了。

JDK1.5中一個變化是類 java.lang.Class是泛型化的。這是把泛型擴展到容器類之外的一個很有意思的例子。

現在,Class有一個類型參數T, 你很可能會問,T 代表什麼?它代表Class對象代表的類型。比如說,

String.class類型代表 ClassString,Serializable.class代表 ClassSerializable。

這可以被用來提高你的反射代碼的類型安全。

特別的,因為 Class的 newInstance() 方法現在返回一個T, 你可以在使用反射創建對象時得到更精確的類型。

比如說,假定你要寫一個工具方法來進行一個資料庫查詢,給定一個SQL語句,並返回一個資料庫中符合查詢條件

的對象集合(collection)。

一個方法是顯式的傳遞一個工廠對象,像下面的代碼:

interface FactoryT {

      public T[] make();

}

public T CollectionT select(FactoryT factory, String statement) { 

       CollectionT result = new ArrayListT();

       /* run sql query using jdbc */

       for ( int i=0; i10; i++ ) { /* iterate over jdbc results */

            T item = factory.make();

            /* use reflection and set all of item』s fields from sql results */

            result.add( item );

       }

       return result;

}

你可以這樣調用:

select(new FactoryEmpInfo(){ 

    public EmpInfo make() { 

        return new EmpInfo();

        }

       } , 」selection string」);

也可以聲明一個類 EmpInfoFactory 來支持介面 Factory:

class EmpInfoFactory implements FactoryEmpInfo { …

    public EmpInfo make() { return new EmpInfo();}

}

然後調用:

select(getMyEmpInfoFactory(), “selection string”);

這個解決方案的缺點是它需要下面的二者之一:

調用處那冗長的匿名工廠類,或為每個要使用的類型聲明一個工廠類並傳遞其對象給調用的地方,這很不自然。

使用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 */

    for ( /* iterate over jdbc results */ ) { 

        Object item = c.newInstance();

        /* use reflection and set all of item』s fields from sql results */

        result.add(item);

    }

        return result;

}

但是這不能給我們返回一個我們要的精確類型的集合。現在Class是泛型的,我們可以寫:

CollectionEmpInfo emps=sqlUtility.select(EmpInfo.class, 」select * from emps」); …

public static T CollectionT select(ClassTc, String sqlStatement) { 

    CollectionT result = new ArrayListT();

    /* run sql query using jdbc */

    for ( /* iterate over jdbc results */ ) { 

        T item = c.newInstance();

        /* use reflection and set all of item』s fields from sql results */

        result.add(item);

    } 

    return result;

}

來通過一種類型安全的方式得到我們要的集合。

這項技術是一個非常有用的技巧,它已成為一個在處理注釋(annotations)的新API中被廣泛使用的習慣用法。

7. 新老代碼兼容

7.1. 為了保證代碼的兼容性,下面的代碼編譯器(javac)允許,類型安全有你自己保證

List l = new ArrayListString();

ListString l = new ArrayList();

7.2. 在將你的類庫升級為范型版本時,慎用協變式返回值。

例如,將代碼

public class Foo { 

    public Foo create(){

        return new Foo();

    }

}

public class Bar extends Foo { 

    public Foo create(){

        return new Bar();

    } 

}

採用協變式返回值風格,將Bar修改為

public class Bar extends Foo { 

    public Bar create(){

        return new Bar();

    } 

}

要小心你類庫的客戶端。

java 泛型的幾種用法

1. public class DAOT {

/**

* 泛型類

* 聲明類的同時聲明泛型類型

* 1.方法的返回值可以是使用聲明的泛型類型

* 2.方法的參數也可以是聲明類的泛型類型

* 3.方法體內可以使用泛型類型

*/

public T get(Integer id){

return null;

}

public void save(T entity){

}

}

2.

/**

* 泛型方法: 在方法聲明時, 同時聲明泛型. 在方法的返回值, 參數列表以及方法體中都可以使用泛型類型.

* public static T T get(Integer id){

* T result = null;

* return result;

* }

* 把指定類型的數組中的元素放入到指定類型的集合中

*/

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/254147.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-12-14 17:40
下一篇 2024-12-14 17:40

相關推薦

  • java client.getacsresponse 編譯報錯解決方法

    java client.getacsresponse 編譯報錯是Java編程過程中常見的錯誤,常見的原因是代碼的語法錯誤、類庫依賴問題和編譯環境的配置問題。下面將從多個方面進行分析…

    編程 2025-04-29
  • Java JsonPath 效率優化指南

    本篇文章將深入探討Java JsonPath的效率問題,並提供一些優化方案。 一、JsonPath 簡介 JsonPath是一個可用於從JSON數據中獲取信息的庫。它提供了一種DS…

    編程 2025-04-29
  • Java騰訊雲音視頻對接

    本文旨在從多個方面詳細闡述Java騰訊雲音視頻對接,提供完整的代碼示例。 一、騰訊雲音視頻介紹 騰訊雲音視頻服務(Cloud Tencent Real-Time Communica…

    編程 2025-04-29
  • Java Bean載入過程

    Java Bean載入過程涉及到類載入器、反射機制和Java虛擬機的執行過程。在本文中,將從這三個方面詳細闡述Java Bean載入的過程。 一、類載入器 類載入器是Java虛擬機…

    編程 2025-04-29
  • Java Milvus SearchParam withoutFields用法介紹

    本文將詳細介紹Java Milvus SearchParam withoutFields的相關知識和用法。 一、什麼是Java Milvus SearchParam without…

    編程 2025-04-29
  • 解決.net 6.0運行閃退的方法

    如果你正在使用.net 6.0開發應用程序,可能會遇到程序閃退的情況。這篇文章將從多個方面為你解決這個問題。 一、代碼問題 代碼問題是導致.net 6.0程序閃退的主要原因之一。首…

    編程 2025-04-29
  • ArcGIS更改標註位置為中心的方法

    本篇文章將從多個方面詳細闡述如何在ArcGIS中更改標註位置為中心。讓我們一步步來看。 一、禁止標註智能調整 在ArcMap中設置標註智能調整可以自動將標註位置調整到最佳顯示位置。…

    編程 2025-04-29
  • Python創建分配內存的方法

    在python中,我們常常需要創建並分配內存來存儲數據。不同的類型和數據結構可能需要不同的方法來分配內存。本文將從多個方面介紹Python創建分配內存的方法,包括列表、元組、字典、…

    編程 2025-04-29
  • Python中init方法的作用及使用方法

    Python中的init方法是一個類的構造函數,在創建對象時被調用。在本篇文章中,我們將從多個方面詳細討論init方法的作用,使用方法以及注意點。 一、定義init方法 在Pyth…

    編程 2025-04-29
  • Java 8中某一周的周一

    Java 8是Java語言中的一個版本,於2014年3月18日發布。本文將從多個方面對Java 8中某一周的周一進行詳細的闡述。 一、數組處理 Java 8新特性之一是Stream…

    編程 2025-04-29

發表回復

登錄後才能評論