導航:首頁 > 編程語言 > java定義泛型方法

java定義泛型方法

發布時間:2023-03-26 21:22:29

① 請教關於java的泛型方法

Java泛型詳解

  1. 概述
    在引入范型之前,Java類型分為原始類型、復雜類型,其中復雜類型分為數組和類。引入范型後臘陸仔,一個復雜類型
    就可以在細分成更多的類型。
    例如原先的類型List,現在在細分成List<Object>, List<String>等更多的類型。
    注意,現在List<Object>, List<String>是兩種不同的類型,
    他們之間沒有繼承關系,即使String繼承了Object。下面的代碼是非法的
    List<String> ls = new ArrayList<String>();
    List<Object> lo = ls;
    這樣設計的原因在於,根據lo的聲明,編譯器允許你向lo中添加任意對象(例如Integer),但是此對象是
    List<String>,破壞了數據類型的完整性。
    在引入范型之前,要在類中的方法支持多個數據類型,就需要對方法進行重載,在引入范型後,可以解決此問題
    (多態),更進一步可以定義多個參數以及返回值之間的關系。
    例如
    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 TestClassDefine<T, S extends T> {
    ....
    }

    2.2 定義待類型參數方法
    在定義帶類型參數的方法時,在緊跟可見范圍修飾(例如public)之後的<>內,指定一個或多個類型參數的名字,同時也可以對類型參數的取值范圍進行限定,多個類型參數之間用,號分隔。
    定義完類型參數後,可以在定義位置之後的方法的任意地方使用類型參數,就像使用普通的類型一樣。
    例如:
    public <T, S extends T> T testGenericMethodDefine(T t, S s){
    ...
    }
    注意:定義帶類型參數的方法,騎主要目的是為了表達多個參數以及返回值之間的關系。例如本例子中T和S的繼承關系, 返回值的類型和第一個類型參數的值相同。
    如輪汪果僅僅是想實現多態,請優先使用通配符解決。通配符的內容見下面章節。
    public <T> void testGenericMethodDefine2(List<T> s){
    ...
    }
    應改為
    public void testGenericMethodDefine2(List<?> s){
    ...
    }

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

    3.1 對帶類型參數的類進行類型參數賦值
    對帶類型參數的類進行類型參數賦值有兩種方式
    第一聲明類變數或者實例化時。例如
    List<String> list;
    list = new ArrayList<String>;
    第二繼承類或者實現介面時。例如
    public class MyList<E> extends ArrayList<E> implements List<E> {...}

    3.2 對帶類型參數方法進行賦值
    當調用范型方法時,編譯器自動對類型參數進行賦值,當不能成功賦值時報編譯錯誤。例如
    public <T> T testGenericMethodDefine3(T t, List<T> list){
    ...
    }
    public <T> T testGenericMethodDefine4(List<T> list1, List<T> list2){
    ...
    }

    Number n = null;
    Integer i = null;
    Object o = null;
    testGenericMethodDefine(n, i);//此時T為Number, S為Integer
    testGenericMethodDefine(o, i);//T為Object, S為Integer

    List<Number> list1 = null;
    testGenericMethodDefine3(i, list1)//此時T為Number

    List<Integer> list2 = null;
    testGenericMethodDefine4(list1, list2)//編譯報錯

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

    List<String> listString;
    List<?> unknownList2 = listString;
    unknownList = unknownList2;
    listString = unknownList;//編譯錯誤

    4. 數組范型
    可以使用帶范型參數值的類聲明數組,卻不可有創建數組
    List<Integer>[] iListArray;
    new ArrayList<Integer>[10];//編譯時錯誤

    5. 實現原理

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

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

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

    6. Class的范型處理
    Java 5之後,Class變成范型化了。
    JDK1.5中一個變化是類 java.lang.Class是泛型化的。這是把泛型擴展到容器類之外的一個很有意思的例子。
    現在,Class有一個類型參數T, 你很可能會問,T 代表什麼?它代表Class對象代表的類型。比如說,
    String.class類型代表 Class<String>,Serializable.class代表 Class<Serializable>。
    這可以被用來提高你的反射代碼的類型安全。
    特別的,因為 Class的 newInstance() 方法現在返回一個T, 你可以在使用反射創建對象時得到更精確的類型。
    比如說,假定你要寫一個工具方法來進行一個資料庫查詢,給定一個SQL語句,並返回一個資料庫中符合查詢條件
    的對象集合(collection)。
    一個方法是顯式的傳遞一個工廠對象,像下面的代碼:
    interface Factory<T> {
    public T[] make();
    }
    public <T> Collection<T> select(Factory<T> factory, String statement) {
    Collection<T> result = new ArrayList<T>();
    /* run sql query using jdbc */
    for ( int i=0; i<10; 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 Factory<EmpInfo>(){
    public EmpInfo make() {
    return new EmpInfo();
    }
    } , 」selection string」);
    也可以聲明一個類 EmpInfoFactory 來支持介面 Factory:
    class EmpInfoFactory implements Factory<EmpInfo> { ...
    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是泛型的,我們可以寫:
    Collection<EmpInfo> emps=sqlUtility.select(EmpInfo.class, 」select * from emps」); ...
    public static <T> Collection<T> select(Class<T>c, String sqlStatement) {
    Collection<T> result = new ArrayList<T>();
    /* 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 ArrayList<String>();
    List<String> 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中在定義泛型泛型方法是方法名前加上泛型有什麼作用,也就是方法前的<T>的作用

static <T> void show(Collection<T> C){
}
//這個泛型方法中定義了一個T類型形參,這個T類型形參可以在該方法內當成普通類型使用。這里無需顯示傳入實際類型參數,無論調用該方法時傳入什麼類型的值,系統都可以判斷出最直接的類型參數。舉個例子給你看可能會更加明白:
import java.util.*;
public class 泛型Test
{
static <T> void show(Collection<T> C)
{
System.out.println("使用泛型 ------->" + C);
}
static void show2(Collection C)
{
System.out.println( C);
}
public static void main(String[] args)
{
Collection<String> C = new HashSet<>();
C.add("第一個元素");
//下面這行代碼調用show方法時會出錯,因為無法通過方法調用轉換將實際參數int轉換為String
//因為編譯器會根據實參推斷類型形參的值,所以系統都可以判斷出最直接的類型參數為String
//C.add(58);
泛型Test.show(C);
泛型Test.show2(C);
Collection d = new HashSet();
//由於java在設計集合時,並不知道我們用它來保存什麼類型的對象,所以便設計成可以保存任
//何類型的對象。同時也帶了問題,集合只是知道它裝了Object類型的值,取出元素時還需要進
//行強制類型轉換,增加了編程復雜度,也容易引發ClassCastException。下面兩行代碼中添加
//兩種不同類型的元素,沒有錯誤。
d.add(2);
d.add("第二個元素");
泛型Test.show(d);
泛型Test.show2(d);
}
}

③ JAVA中的泛型用法一種: <T> 返回值用法。

1、對於泛型方法來說,是可以接收不同類型的參數,比如下圖,使用泛型來操作List集合,然後向List中添加一條數據,看是否可以添加成功,創建泛型方法如圖所示。

④ Java中怎樣使用泛型T類的方法

調用泛型方法語法格式如下publicclassExampleA{
public<T>voidf(Tx){
System.out.println(x.getClass().getName());
}

publicstaticvoidmain(String[]args){
ExampleAea=newExampleA();
ea.f("");
ea.f(10);
ea.f('a');
ea.f(ea);
}
}

輸出結果:

  1. java.lang.String

  2. java.lang.Integer

  3. java.lang.Character

  4. ExampleA

使用泛型方法時,不必指明參數類型,編譯器會自己找出具體的類型。泛型方法除了定義不同,調用就像普通方法一樣。

需要注意,一個static方法,無法訪問泛型類的類型參數,所以,若要static方法需要使用泛型能力,必須使其成為泛型方法。

⑤ java怎樣聲明泛型方法

把泛型加在修飾符的前面,具體代碼如下:

public<T>voidshow(Tt){
//dosomeing
}

備註:一般泛型方法的參數也是泛型的,否則這個泛型方法沒有意義,

⑥ java 泛型類和泛型方法的區別

在java中

泛型類:是在實例化類的時候指明泛型的具體類型。
泛型方法:是在調用方法的時候指明泛型的具體類型。
定義泛型方法時:必須在返回值前邊加一個<T>,來聲明這是一個泛型方法,持有一個泛型T,然後才可以用泛型T作為方法的返回值,泛型方法不是僅僅可以有一個參數Class<T>,可以根據需要添加其他參數。
定義泛型類時:Class<T>的作用就是指明泛型的具體類型,而Class<T>類型的變數C,可以用來創建泛型類的對象

⑦ java中什麼叫泛型

有泛型參數,泛型方法,這篇文件寫的很好,你仔細 讀一下,可以多讀幾次,總會有收獲滴
java泛型
java泛型

什麼是泛型?
泛型(Generic type 或者 generics)是對 Java 語言的類型系統的一種擴展,以支持創建可以按類型進行參數化的類。可以把類型參數看作是使用參數化類型時指定的類型的一個佔位符,就像方法的形式參數是運行時傳遞的值的佔位符一樣。
可以在集合框架(Collection framework)中看到泛型的動機。例如,Map 類允許您向一個 Map 添加任意類的對象,即使最常見的情況是在給定映射(map)中保存某個特定類型(比如 String)的對象。
因為 Map.get() 被定義為返回 Object,所以一般必須將 Map.get() 的結果強制類型轉換為期望的類型,如下面的代碼所示:
Map m = new HashMap();
m.put("key", "blarg");
String s = (String) m.get("key");
要讓程序通過編譯,必須將 get() 的結果強制類型轉換為 String,並且希望結果真的是一個 String。但是有可能某人已經在該映射中保存了不是 String 的東西,這樣的話,上面的代碼將會拋出 ClassCastException。
理想情況下,您可能會得出這樣一個觀點,即 m 是一個 Map,它將 String 鍵映射到 String 值。這可以讓您消除代碼中的強制類型轉換,同時獲得一個附加的類型檢查層,該檢查層可以防止有人將錯誤類型的鍵或值保存在集合中。這就是泛型所做的工作。

泛型的好處
Java 語言中引入泛型是一個較大的功能增強。不僅語言、類型系統和編譯器有了較大的變化,以支持泛型,而且類庫也進行了大翻修,所以許多重要的類,比如集合框架,都已經成為泛型化的了。這帶來了很多好處:
類型安全。 泛型的主要目標是提高 Java 程序的類型安全。通過知道使用泛型定義的變數的類型限制,編譯器可以在一個高得多的程度上驗證類型假設。沒有泛型,這些假設就只存在於程序員的頭腦中(或者如果幸運的話,還存在於代碼注釋中)。
Java 程序中的一種流行技術是定義這樣的集合,即它的元素或鍵是公共類型的,比如「String 列表」或者「String 到 String 的映射」。通過在變數聲明中捕獲這一附加的類型信息,泛型允許編譯器實施這些附加的類型約束。類型錯誤現在就可以在編譯時被捕獲了,而不是在運行時當作 ClassCastException 展示出來。將類型檢查從運行時挪到編譯時有助於您更容易找到錯誤,並可提高程序的可靠性。
消除強制類型轉換。 泛型的一個附帶好處是,消除源代碼中的許多強制類型轉換。這使得代碼更加可讀,並且減少了出錯機會。
盡管減少強制類型轉換可以降低使用泛型類的代碼的羅嗦程度,但是聲明泛型變數會帶來相應的羅嗦。比較下面兩個代碼例子。
該代碼不使用泛型:
List li = new ArrayList();
li.put(new Integer(3));
Integer i = (Integer) li.get(0);

該代碼使用泛型:
List<Integer> li = new ArrayList<Integer>();
li.put(new Integer(3));
Integer i = li.get(0);

在簡單的程序中使用一次泛型變數不會降低羅嗦程度。但是對於多次使用泛型變數的大型程序來說,則可以累積起來降低羅嗦程度。
潛在的性能收益。 泛型為較大的優化帶來可能。在泛型的初始實現中,編譯器將強制類型轉換(沒有泛型的話,程序員會指定這些強制類型轉換)插入生成的位元組碼中。但是更多類型信息可用於編譯器這一事實,為未來版本的 JVM 的優化帶來可能。
由於泛型的實現方式,支持泛型(幾乎)不需要 JVM 或類文件更改。所有工作都在編譯器中完成,編譯器生成類似於沒有泛型(和強制類型轉換)時所寫的代碼,只是更能確保類型安全而已。

泛型用法的例子
泛型的許多最佳例子都來自集合框架,因為泛型讓您在保存在集合中的元素上指定類型約束。考慮這個使用 Map 類的例子,其中涉及一定程度的優化,即 Map.get() 返回的結果將確實是一個 String:

Map m = new HashMap();
m.put("key", "blarg");
String s = (String) m.get("key");

如果有人已經在映射中放置了不是 String 的其他東西,上面的代碼將會拋出 ClassCastException。泛型允許您表達這樣的類型約束,即 m 是一個將 String 鍵映射到 String 值的 Map。這可以消除代碼中的強制類型轉換,同時獲得一個附加的類型檢查層,這個檢查層可以防止有人將錯誤類型的鍵或值保存在集合中。
下面的代碼示例展示了 JDK 5.0 中集合框架中的 Map 介面的定義的一部分:

public interface Map<K, V> {
public void put(K key, V value);
public V get(K key);
}
注意該介面的兩個附加物:
類型參數 K 和 V 在類級別的規格說明,表示在聲明一個 Map 類型的變數時指定的類型的佔位符。
在 get()、put() 和其他方法的方法簽名中使用的 K 和 V。
為了贏得使用泛型的好處,必須在定義或實例化 Map 類型的變數時為 K 和 V 提供具體的值。以一種相對直觀的方式做這件事:
Map<String, String> m = new HashMap<String, String>();
m.put("key", "blarg");
String s = m.get("key");
當使用 Map 的泛型化版本時,您不再需要將 Map.get() 的結果強制類型轉換為 String,因為編譯器知道 get() 將返回一個 String。
在使用泛型的版本中並沒有減少鍵盤錄入;實際上,比使用強制類型轉換的版本需要做更多鍵入。使用泛型只是帶來了附加的類型安全。因為編譯器知道關於您將放進 Map 中的鍵和值的類型的更多信息,所以類型檢查從執行時挪到了編譯時,這會提高可靠性並加快開發速度。

向後兼容
在 Java 語言中引入泛型的一個重要目標就是維護向後兼容。盡管 JDK 5.0 的標准類庫中的許多類,比如集合框架,都已經泛型化了,但是使用集合類(比如 HashMap 和 ArrayList)的現有代碼將繼續不加修改地在 JDK 5.0 中工作。當然,沒有利用泛型的現有代碼將不會贏得泛型的類型安全好處。

二 泛型基礎
類型參數
在定義泛型類或聲明泛型類的變數時,使用尖括弧來指定形式類型參數。形式類型參數與實際類型參數之間的關系類似於形式方法參數與實際方法參數之間的關系,只是類型參數表示類型,而不是表示值。
泛型類中的類型參數幾乎可以用於任何可以使用類名的地方。例如,下面是 java.util.Map 介面的定義的摘錄:
public interface Map<K, V> {
public void put(K key, V value);
public V get(K key);
}
Map 介面是由兩個類型參數化的,這兩個類型是鍵類型 K 和值類型 V。(不使用泛型)將會接受或返回 Object 的方法現在在它們的方法簽名中使用 K 或 V,指示附加的類型約束位於 Map 的規格說明之下。
當聲明或者實例化一個泛型的對象時,必須指定類型參數的值:
Map<String, String> map = new HashMap<String, String>();
注意,在本例中,必須指定兩次類型參數。一次是在聲明變數 map 的類型時,另一次是在選擇 HashMap 類的參數化以便可以實例化正確類型的一個實例時。
編譯器在遇到一個 Map<String, String> 類型的變數時,知道 K 和 V 現在被綁定為 String,因此它知道在這樣的變數上調用 Map.get() 將會得到 String 類型。
除了異常類型、枚舉或匿名內部類以外,任何類都可以具有類型參數。

命名類型參數
推薦的命名約定是使用大寫的單個字母名稱作為類型參數。這與 C++ 約定有所不同(參閱 附錄 A:與 C++ 模板的比較),並反映了大多數泛型類將具有少量類型參數的假定。對於常見的泛型模式,推薦的名稱是:
K —— 鍵,比如映射的鍵。
V —— 值,比如 List 和 Set 的內容,或者 Map 中的值。
E —— 異常類。
T —— 泛型。

泛型不是協變的
關於泛型的混淆,一個常見的來源就是假設它們像數組一樣是協變的。其實它們不是協變的。List<Object> 不是 List<String> 的父類型。
如果 A 擴展 B,那麼 A 的數組也是 B 的數組,並且完全可以在需要 B[] 的地方使用 A[]:
Integer[] intArray = new Integer[10];
Number[] numberArray = intArray;
上面的代碼是有效的,因為一個 Integer 是 一個 Number,因而一個 Integer 數組是 一個 Number 數組。但是對於泛型來說則不然。下面的代碼是無效的:
List<Integer> intList = new ArrayList<Integer>();
List<Number> numberList = intList; // invalid
最初,大多數 Java 程序員覺得這缺少協變很煩人,或者甚至是「壞的(broken)」,但是之所以這樣有一個很好的原因。如果可以將 List<Integer> 賦給 List<Number>,下面的代碼就會違背泛型應該提供的類型安全:
List<Integer> intList = new ArrayList<Integer>();
List<Number> numberList = intList; // invalid
numberList.add(new Float(3.1415));
因為 intList 和 numberList 都是有別名的,如果允許的話,上面的代碼就會讓您將不是 Integers 的東西放進 intList 中。但是,正如下一屏將會看到的,您有一個更加靈活的方式來定義泛型。

類型通配符
假設您具有該方法:
void printList(List l) {
for (Object o : l)
System.out.println(o);
}
上面的代碼在 JDK 5.0 上編譯通過,但是如果試圖用 List<Integer> 調用它,則會得到警告。出現警告是因為,您將泛型(List<Integer>)傳遞給一個只承諾將它當作 List(所謂的原始類型)的方法,這將破壞使用泛型的類型安全。
如果試圖編寫像下面這樣的方法,那麼將會怎麼樣?
void printList(List<Object> l) {
for (Object o : l)
System.out.println(o);
}
它仍然不會通過編譯,因為一個 List<Integer> 不是 一個 List<Object>(正如前一屏 泛型不是協變的 中所學的)。這才真正煩人 —— 現在您的泛型版本還沒有普通的非泛型版本有用!
解決方案是使用類型通配符:
void printList(List<?> l) {
for (Object o : l)
System.out.println(o);
}
上面代碼中的問號是一個類型通配符。它讀作「問號」。List<?> 是任何泛型 List 的父類型,所以您完全可以將 List<Object>、List<Integer> 或 List<List<List<Flutzpah>>> 傳遞給 printList()。

類型通配符的作用
前一屏 類型通配符 中引入了類型通配符,這讓您可以聲明 List<?> 類型的變數。您可以對這樣的 List 做什麼呢?非常方便,可以從中檢索元素,但是不能添加元素。原因不是編譯器知道哪些方法修改列表哪些方法不修改列表,而是(大多數)變化的方法比不變化的方法需要更多的類型信息。下面的代碼則工作得很好:
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(42));
List<?> lu = li;
System.out.println(lu.get(0));
為什麼該代碼能工作呢?對於 lu,編譯器一點都不知道 List 的類型參數的值。但是編譯器比較聰明,它可以做一些類型推理。在本例中,它推斷未知的類型參數必須擴展 Object。(這個特定的推理沒有太大的跳躍,但是編譯器可以作出一些非常令人佩服的類型推理,後面就會看到(在 底層細節 一節中)。所以它讓您調用 List.get() 並推斷返回類型為 Object。
另一方面,下面的代碼不能工作:
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(42));
List<?> lu = li;
lu.add(new Integer(43)); // error
在本例中,對於 lu,編譯器不能對 List 的類型參數作出足夠嚴密的推理,以確定將 Integer 傳遞給 List.add() 是類型安全的。所以編譯器將不允許您這么做。
以免您仍然認為編譯器知道哪些方法更改列表的內容哪些不更改列表內容,請注意下面的代碼將能工作,因為它不依賴於編譯器必須知道關於 lu 的類型參數的任何信息:
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(42));
List<?> lu = li;
lu.clear();

泛型方法
(在 類型參數 一節中)您已經看到,通過在類的定義中添加一個形式類型參數列表,可以將類泛型化。方法也可以被泛型化,不管它們定義在其中的類是不是泛型化的。
泛型類在多個方法簽名間實施類型約束。在 List<V> 中,類型參數 V 出現在 get()、add()、contains() 等方法的簽名中。當創建一個 Map<K, V> 類型的變數時,您就在方法之間宣稱一個類型約束。您傳遞給 add() 的值將與 get() 返回的值的類型相同。
類似地,之所以聲明泛型方法,一般是因為您想要在該方法的多個參數之間宣稱一個類型約束。例如,下面代碼中的 ifThenElse() 方法,根據它的第一個參數的布爾值,它將返回第二個或第三個參數:
public <T> 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<V extends Number> { ... }
編譯器允許您創建 Matrix<Integer> 或 Matrix<Float> 類型的變數,但是如果您試圖定義 Matrix<String> 類型的變數,則會出現錯誤。類型參數 V 被判斷為由 Number 限制 。在沒有類型限制時,假設類型參數由 Object 限制。這就是為什麼前一屏 泛型方法 中的例子,允許 List.get() 在 List<?> 上調用時返回 Object,即使編譯器不知道類型參數 V 的類型。

三 一個簡單的泛型類
編寫基本的容器類
此時,您可以開始編寫簡單的泛型類了。到目前為止,泛型類最常見的用例是容器類(比如集合框架)或者值持有者類(比如 WeakReference 或 ThreadLocal)。我們來編寫一個類,它類似於 List,充當一個容器。其中,我們使用泛型來表示這樣一個約束,即 Lhist 的所有元素將具有相同類型。為了實現起來簡單,Lhist 使用一個固定大小的數組來保存值,並且不接受 null 值。
Lhist 類將具有一個類型參數 V(該參數是 Lhist 中的值的類型),並將具有以下方法:
public class Lhist<V> {
public Lhist(int capacity) { ... }
public int size() { ... }
public void add(V value) { ... }
public void remove(V value) { ... }
public V get(int index) { ... }
}
要實例化 Lhist,只要在聲明時指定類型參數和想要的容量:
Lhist<String> stringList = new Lhist<String>(10);

實現構造函數
在實現 Lhist 類時,您將會遇到的第一個攔路石是實現構造函數。您可能會像下面這樣實現它:
public class Lhist<V> {
private V[] array;
public Lhist(int capacity) {
array = new V[capacity]; // illegal
}
}
這似乎是分配後備數組最自然的一種方式,但是不幸的是,您不能這樣做。具體原因很復雜,當學習到 底層細節 一節中的「擦除」主題時,您就會明白。分配後備數組的實現方式很古怪且違反直覺。下面是構造函數的一種可能的實現(該實現使用集合類所採用的方法):
public class Lhist<V> {
private V[] array;
public Lhist(int capacity) {
array = (V[]) new Object[capacity];
}
}

另外,也可以使用反射來實例化數組。但是這樣做需要給構造函數傳遞一個附加的參數 —— 一個類常量,比如 Foo.class。後面在 Class<T> 一節中將討論類常量。

實現方法
實現 Lhist 的方法要容易得多。下面是 Lhist 類的完整實現:
public class Lhist<V> {
private V[] array;
private int size;
public Lhist(int capacity) {
array = (V[]) new Object[capacity];
}
public void add(V value) {
if (size == array.length)
throw new IndexOutOfBoundsException(Integer.toString(size));
else if (value == null)
throw new NullPointerException();
array[size++] = value;
}
public void remove(V value) {
int removalCount = 0;
for (int i=0; i<size; i++) {
if (array[i].equals(value))
++removalCount;
else if (removalCount > 0) {
array[i-removalCount] = array[i];
array[i] = null;
}
}
size -= removalCount;
}
public int size() { return size; }
public V get(int i) {
if (i >= size)
throw new IndexOutOfBoundsException(Integer.toString(i));
return array[i];
}
}
注意,您在將會接受或返回 V 的方法中使用了形式類型參數 V,但是您一點也不知道 V 具有什麼樣的方法或域,因為這些對泛型代碼是不可知的。

使用 Lhist 類
使用 Lhist 類很容易。要定義一個整數 Lhist,只需要在聲明和構造函數中為類型參數提供一個實際值即可:
Lhist<Integer> li = new Lhist<Integer>(30);
編譯器知道,li.get() 返回的任何值都將是 Integer 類型,並且它還強制傳遞給 li.add() 或 li.remove() 的任何東西都是 Integer。除了實現構造函數的方式很古怪之外,您不需要做任何十分特殊的事情以使 Lhist 是一個泛型類。

⑧ java泛型中,定義泛型方法格式:public <T> T fun(T t),這幾個T分別是什麼意

第一個<T>表示該方法是泛型方法,第二個T表示返回值類型是T類型。

⑨ Java介面內定義泛型方法如何實現

介面 IHuman.java

public interface IHuman<K, V> {
V execute(K request) throws Exception;
}

實現 MaleHuman.java
public class MaleHuman implements IHuman<String, String> {
@Override
public String execute(String request) throws Exception {
return ...;
}
}

按照以上介面內的泛型方法,能進行子類實現也不會報錯。
但是介面內的泛型方法換種寫法,就不知道如何寫實現了。
介面 IHuman.java (改)
public interface IHuman {
<K, V> V execute(K request) throws Exception;
}

閱讀全文

與java定義泛型方法相關的資料

熱點內容
天貓程序員有什麼用 瀏覽:553
第三百章挺近劉濤 瀏覽:236
先收母親收女兒的小說 瀏覽:35
無牙仔電影叫什麼名字 瀏覽:505
樹莓派python爬蟲 瀏覽:67
穿越長征之紅星戰將 瀏覽:264
80台伺服器要用什麼防火牆 瀏覽:230
安卓應用是什麼進制 瀏覽:77
穿越民國的二道販子 瀏覽:357
伺服器出現狀況怎麼辦 瀏覽:408
有一本小說女主叫姜什麼 瀏覽:866
pdf轉換器軟體 瀏覽:640
命令提示符的雙引號 瀏覽:186
命令行運行編譯器 瀏覽:548
linux升級php7 瀏覽:123
love法國原版電影 瀏覽:62
什麼app不用驗證手機登錄 瀏覽:481
python抓包http 瀏覽:893
安卓手機如何屏蔽黃色內容 瀏覽:176
有損壓縮失真嗎 瀏覽:412