當前位置:首頁 » 編程語言 » java泛型

java泛型

發布時間: 2022-01-12 07:05:34

1. java泛型

java泛型是1.5引進的一個新概念.
本題對於"? super T"和"? extends T",我從書上摘個經典的例子給你看看,如果不能理解,那麼你就參考以下書籍慢慢體會,循序漸進!

"? super T"和"? extends T",都是java泛型通配符,而用法又有區別,
還有super 和extends 不是java類關系中的超類和繼承的意思,他是通配符的下限和上限限制.

下面看一個通配符得高級用法:
在這一部分,我們來考慮一些通配符得高級用法。我們已經看到了上限通配符在從一個數據結構中進行讀取的幾個例子。現在考慮相反的情況,一個只寫的數據結構。

介面Sink是這種情況的一個簡單例子。

interface Sink<T> {

void flush(T t);

}

我們可以想像他被如下面的代碼一樣使用。方法writeAll() 被設計來把集合coll的所有元素flush到sink snk,並且返回最後一個flush的元素。

public static <T> T writeAll(Collection<T> coll, Sink<T> snk) {

T last = null;

for (T t : coll) {

last = t;

snk.flush(last);

}

return last;

}

Sink<Object> s;

Collection<String> cs;

String str = writeAll(cs, s); // 非法的調用!!

像上面所寫,writeAll() 的調用是非法的,因為沒有有效的類型參數可以被推斷出來。String 或 Object都不是T的合適的類型,因為Collection的元素和 Sink的元素必須是同樣的類型。

我們可以解決這個問題,通過使用通配符來修改writeAll()的方法簽名,如下:

<T> T writeAll(Collection<? extends T> coll, Sink<T> snk) { … }

String str = writeAll(cs, s); //可以調用但是返回值類型錯誤

這個調用現在是合法的,但是賦值產生錯誤,因為推斷出的返回值類型是 Object因為T 匹配了Sink的類型,Object。

解決方案是使用一種我們還沒有見過的有限制的通配符:有下限的通配符。語法 ? super T 表示T的一個未知的父類(或者是T自己)。這跟我們用? extends T 表示T的一個未知的子類是對應的。

<T> T writeAll(Collection<T> coll, Sink<? super T> snk) { … }

String str = writeAll(cs, s); // YES!!!

使用這個語法,這個調用是合法的,推斷出來的T是String,正是我們想要的。

現在讓我們看一個更現實的例子。一個 java.util.TreeSet<E> 代表一個有序的元素是E類型的樹。創建一個TreeSet的一個方法是傳遞一個 Comparator 對象給構造函數。這個Comparator將會用來按照需要對TreeSet進行排序。

TreeSet(Comparator<E> c)

Comparator 介面是核心:

interface Comparator<T> { int compare(T fst, T snd); }

假定我們要創建一個 TreeSet<String> 並傳遞一個合適的 Comparator,我們需要傳一個能比較String的Comparator。這可以是一個 Comparator<String>,也可以是一個 Comparator<Object>。然而我們不能用Comparator<Object>來調用上面的構造函數。我們可以使用一個有下限的通配符來得到我們需要的靈活性:

TreeSet(Comparator<? super E> c)

這允許任何可用的Comparator被傳遞進去。

作為使用下限通配符最終的例子,讓我們來看看方法 Collections.max(),它返回一個集合中的最大的元素。

現在,為了讓max()能工作,傳進來的集合中的所有元素必須實現 Comparatable介面。而且,他們必須都能夠被彼此比較(all be comparable to each other)。第一個嘗試是:

public static <T extends Comparable<T>> T max(Collection<T> coll)

就是說,方法的參數是某一個能和自己進行比較的T的集合。這限制太嚴格了。

為什麼?考慮一個能和任何對象進行比較的類型:

class Foo implements Comparable<Object> {...} ...

Collection<Foo> cf = ...;

Collections.max(cf); // 應該能工作

cf 中的每個元素都可以和每個cf中的其他元素進行比較,因為每個這樣的元素都是一個Foo,它可以和任意的對象進行比較,也可以和另一個Foo進行比較。

但是,使用上面的方法簽名,我們發現這個調用被拒絕。推斷出來的類型必須是Foo,但是Foo沒有實現介面 Comparable<Foo>。

T 精確的(exactly)和自己能比較是不需要的。所需要的是 T能夠和它的父類中的一個進行比較,這導出:(註:Collections.max()的實際方法簽名更復雜,我們在第10部分再討論。)

public static <T extends Comparable<? super T>> T max(Collection<T> coll)

這個推論對大多數想讓 Comparable 對任意類型生效的用法中都有效:你總是應該使用 Comparable<? super T>。

總之,如果你有一個只使用類型參數T作為參數的API,它的使用應該利用下限通配符( ? super T )的好處。相反的,如果API只返回T,你應該使用上限通配符( ? extends T )來給你的客戶端更大的靈活性。

(原文:This reasoning applies to almost any usage of Comparable that is intended to work for arbitrary types: You always want to use Comparable<? super T>.

In general, if you have an API that only uses a type parameter T as an argument, its uses should take advantage of lower bounded wildcards (? super T). Conversely, if the API only returns T, you'll give your clients more flexibility by using upper bounded wildcards (? extends T). )。

如果你想比較深刻的了解java泛型那麼
建議你看看<Java1.5泛型指南>
中文鏈接地址:http://blog.csdn.net/explorers/archive/2005/08/15/454837.aspx#_Toc111865968
英文pdf格式地址:http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf

2. Java泛型

你的代碼應該有錯誤吧,我改了一下
package Test;
//此段代碼中Automobile a = h3.get(); 是什麼意思?
class Automobile {
}
public class GenericHolder<T> {
private T a;
public GenericHolder(T a) {
this.a = a;
}
public void set(T a) {
this.a = a;
}
public T get() {
return a;
}
public static void main(String[] args) {
GenericHolder<Automobile> h3 = new GenericHolder<>(new Automobile());
Automobile a = h3.get(); // No cast needed
// h3.set("The String"); // Error
// h3.set(1); // Error
}
}
首先泛型T你得先告訴系統,我的T代表什麼,因為java並沒有T這個類型,必須在指定類型之前先聲明這個類的泛型T。即你給我傳什麼類型,我的T就代表什麼類型,所以你在創建對象時在構造方法里傳入了一個Automobile的對象,所以T就代表Automobile的類型。
即 GenericHolder<Automobile> h3 = new GenericHolder<>(new Automobile());等價於GenericHolder h3=new GenericHolder();h3.a=new Automobile(); 也就是GenericHolder類中的成員變數a的值是Automobile的一個對象。所以Automobile a = h3.get();的意思就是調用h3的get()方法得到a變數並賦值給Automobile類型的變數a。
歡迎追問

3. java泛型什麼是什麼

java的泛型,只是編譯時作為類型檢查,一旦編譯完成,泛型就會被擦除,在運行期間是得不到泛型的信息的,包括它的類型參數。有時候我們需要用到泛型的類型參數,反射看起來是取不到的,因反射在運行期間執行,但那時已無泛型的信息。反射的概念:主要是指程序可以訪問,檢測和修改它本身狀態或行為的一種能力,並能根據自身行為的狀態和結果,調整或修改應用所描述行為的狀態和相關的語義。

4. java泛型的泛型方法

是否擁有泛型方法,與其所在的類是否泛型沒有關系。要定義泛型方法,只需將泛型參數列表置於返回值前。如: 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);}}輸出結果:
java.lang.String
java.lang.Integer
java.lang.Character
ExampleA
使用泛型方法時,不必指明參數類型,編譯器會自己找出具體的類型。泛型方法除了定義不同,調用就像普通方法一樣。
需要注意,一個static方法,無法訪問泛型類的類型參數,所以,若要static方法需要使用泛型能力,必須使其成為泛型方法。

5. 什麼是java泛型 最好舉個例子

泛型其實很簡單,比如一個List聲明為

List<String> list = new ArrayList<String>();

那麼,這個List裡面只允許存放String類型的變數。

比如
list.add("string"); //這個是正確的
list.add(123); //這個錯了,因為123是int型。

6. 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 中工作。當然,沒有利用泛型的現有代碼將不會贏得泛型的類型安全好處。

7. java中什麼是泛型,怎麼用泛型

最簡單的運用:List<String> list = new ArrayList<String>();
這個是什麼意思?
意思就是list只裝String類型的數據,別的,裝不進去
然後你就會覺得這個好像有點封裝的意思,比如LIst<Student>,封裝學生類
所以,所謂泛型就是廣泛的數據類型,你可以把它理解成封裝

8. 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 是一個泛型類。

9. java中的泛型 求詳細解釋

1、Java泛型
其實Java的泛型就是創建一個用類型作為參數的類。就象我們寫類的方法一樣,方法是這樣的method(String str1,String str2 ),方法中參數str1、str2的值是可變的。而泛型也是一樣的,這樣寫class Java_Generics<K,V>,這里邊的K和V就象方法中的參數str1和str2,也是可變。下面看看例子:
//code list 1
import Java.util.Hashtable;
class TestGen0<K,V>{
public Hashtable<K,V> h=new Hashtable<K,V>();
public void put(K k, V v) {
h.put(k,v);
}
public V get(K k) {
return h.get(k);
}
public static void main(String args[]){
TestGen0<String,String> t=new TestGen0<String,String>();
t.put("key", "value");
String s=t.get("key");
System.out.println(s);
}
}
正確輸出:value
這只是個例子(Java中集合框架都泛型化了,這里費了2遍事.),不過看看是不是創建一個用類型作為參數的類,參數是K,V,傳入的「值」是String類型。這個類他沒有特定的待處理型別,以前我們定義好了一個類,在輸入輸入參數有所固定,是什麼型別的有要求,但是現在編寫程序,完全可以不制定參數的類型,具體用的時候來確定,增加了程序的通用性,像是一個模板。
呵呵,類似C++的模板(類似)。
1.1. 泛型通配符
下面我們先看看這些程序:
//Code list 2
void TestGen0Medthod1(List l) {
for (Object o : l)
System.out.println(o);
}
看看這個方法有沒有異議,這個方法會通過編譯的,假如你傳入String,就是這樣List<String>。
接著我們調用它,問題就出現了,我們將一個List<String>當作List傳給了方法,JVM會給我們一個警告,說這個破壞了類型安全,因為從List中返回的都是Object類型的,而讓我們再看看下面的方法。
//Code list 3
void TestGen0Medthod1(List<String> l) {
for (Object o : l)
System.out.println(o);
}
因為這里的List<String>不是List<Object>的子類,不是String與Object的關系,就是說List<String>不隸屬於list<Object>,他們不是繼承關系,所以是不行的,這里的extends是表示限制的。
類型通配符是很神奇的,List<?>這個你能為他做什麼呢?怎麼都是「?」,它似乎不確定,他總不能返回一個?作為類型的數據吧,是啊他是不會返回一個「?」來問程序員的?JVM會做簡單的思考的,看看代碼吧,更直觀些。
//code list 4
List<String> l1 = new ArrayList<String>();
li.add(「String」);
List<?> l2 = l1;
System.out.println(l1.get(0));
這段代碼沒問題的,l1.get(0)將返回一個Object。
1.2. 編寫泛型類要注意:
1) 在定義一個泛型類的時候,在 「<>」之間定義形式類型參數,例如:「class TestGen<K,V>」,其中「K」 , 「V」不代表值,而是表示類型。
2) 實例化泛型對象的時候,一定要在類名後面指定類型參數的值(類型),一共要有兩次書寫。例如:
TestGen<String,String> t=new TestGen<String,String>();
3) 泛型中<K extends Object>,extends並不代表繼承,它是類型範圍限制。
2、泛型與數據類型轉換
2.1. 消除類型轉換
上面的例子大家看到什麼了,數據類型轉換的代碼不見了。在以前我們經常要書寫以下代碼,如:
//code list 5
import Java.util.Hashtable;
class Test {
public static void main(String[] args) {
Hashtable h = new Hashtable();
h.put("key", "value");
String s = (String)h.get("key");
System.out.println(s);
}
}
這個我們做了類型轉換,是不是感覺很煩的,並且強制類型轉換會帶來潛在的危險,系統可能會拋一個ClassCastException異常信息。在JDK5.0中我們完全可以這么做,如:
//code list 6
import Java.util.Hashtable;
class Test {
public static void main(String[] args) {
Hashtable<String,Integer> h = new Hashtable<String,Integer> ();
h.put("key", new Integer(123));
int s = h.get("key").intValue();
System.out.println(s);
}
}
這里我們使用泛化版本的HashMap,這樣就不用我們來編寫類型轉換的代碼了,類型轉換的過程交給編譯器來處理,是不是很方便,而且很安全。上面是String映射到String,也可以將Integer映射為String,只要寫成HashTable<Integer,String> h=new HashTable<Integer,String>();h.get(new Integer(0))返回value。果然很方便。

10. java泛型的用法給高分,謝謝

泛型就是指定容器中必須裝什麼類型的東西,把運行時會產生的異常提前放到了編碼階段。
你可以不加泛型,然後程序運行出錯之後再進行修改。加了泛型之後不用運行,寫代碼的時候就能知道哪的類型錯了。
純手打

熱點內容
安卓手機怎麼轉移蘋果系統 發布:2024-09-17 04:19:07 瀏覽:934
sql從表中查詢數據 發布:2024-09-17 04:19:07 瀏覽:972
米粒泥解壓 發布:2024-09-17 04:19:01 瀏覽:729
2010word加密方法 發布:2024-09-17 04:17:36 瀏覽:282
crv哪個配置性價比高2021 發布:2024-09-17 04:07:51 瀏覽:36
wincc圖形編譯在哪裡 發布:2024-09-17 03:58:26 瀏覽:978
androidubuntu 發布:2024-09-17 03:50:27 瀏覽:702
識夢源碼 發布:2024-09-17 03:50:18 瀏覽:27
諾基亞密碼忘了打什麼電話 發布:2024-09-17 03:27:09 瀏覽:556
樹深度優先演算法 發布:2024-09-17 03:26:58 瀏覽:474