當前位置:首頁 » 編程語言 » java范形

java范形

發布時間: 2022-10-03 22:13:33

java泛型類和泛型方法

1、什麼是泛型?
泛型簡言之就是類型參數化,不指定類型,運行時傳入類型。
如果業務需求有沒有可能不指定類型?有沒有可能在運行時才知道具體的類型是什麼?
所以,就出現了泛型。

public class Container<K, V> {
private K key;
private V value;
public Container(K k, V v) {
key = k;
value = v;
}
public K getKey() {
return key;
}
public void setKey(K key) {
this.key = key;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
}

編譯期,是無法知道K和V具體是什麼類型,只有在運行時才會真正根據類型來構造和分配內存。這就是泛型。

㈡ java的泛型有沒有用

當然有用,其實泛型存在的意義就好比抽象類,抽象類是用來規范子類。而泛型對於實現代碼復用,提高開發效率幫助很大。
當然不用泛型可不可以,答案是可以,其實很多程序員開發多年都忘記使用泛型的方做開發,這是一個深度問題,而不是廣議問題。要想弄懂泛型,就必須要知道它存在的意義。
在項目研發中,經常會出現一個方法,多種不同類型的對象進行調用,簡單的做法就是為各自的類型寫上一個方法,獨立調用,比如:
//兩個整數相加
public int plus(int a, int b){
return a+b;
}
//兩個字元串相連接
public string connectStr(string str1, string str2){
return str + str2
}
上面是兩個函數,一個是整數的相加,一個是字元串的連接,當然,這是很簡單兩個函數;但是可以看出兩個函數的共同之處,都是相加,"+"對於字元串來講是字元串的連接,但是可以把這兩個函數寫成一個函數,如下:
public T plusTwoObj(T objT1, T objT2){
return objT1 + obT2;
}
這個函數中出現的T,其實就是一個佔位符,也就是這個位置由T來代替;類似於佔座,它起到的作用只是佔座,不會有實際的意義,最終這個位置是由其他人來坐的;比如:T給int佔座,給string佔座,給float佔座,給自定義類型佔座,實際的操作是由其它類型來處理的,其它類型會代替它;
這個函數是由兩個函數提出的公有函數,這樣,它就不局限於某一個類型的函數進行相加處理,而是許多類型可通用的方法,所以,泛型有代碼復用、提高研發效率的作用;而定義成泛型,而不用所有類型基類 object類型呢,就涉及數據類型轉換的問題,類型轉換的拆箱和裝箱需要消耗大量內存和cpu資源。
泛型需要很系統的來解釋,幾句話可能說不清,建議你還是把教材視頻等多翻出來看看。建議把泛型和抽象類結合在一起看,兩者的相似點和區別。

㈢ java中泛型的使用

泛型可以避免強制類型轉換,設定集合對象中存儲的對象類型。
比如List<String> list = new ArrayList<String>();
那在這個list中,只能存儲字元串對象。

㈣ java課程分享JAVA泛型淺談



1. 概述

在引入泛型之前,Java類型分為原始類型、復雜類型,其中復雜類型分為數組和類。java課程http://www.kmbdqn.com/發現引入范型後,一個復雜類型

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

例如原先的類型List,現在在細分成List<Object>, List<String>等更多的類型。

注意,現在List<Object>, List<String>是兩種不同的類型,

他們之間沒有繼承關系,即使String繼承了Object。


2. 定義&使用

類型參數的命名風格為:

推薦你用簡練的名字作為形式類型參數的名字(如果可能,單個字元)。最好避免小寫字母,這使它和其他的普通的形式參數很容易被區分開來。使用T代表類型,無論何時都沒有比這更具體的類型來區分它。這經常見於泛型方法。如果有多個類型參數,我們

可能使用字母表中T的臨近的字母,比如S。 如果一個泛型函數在一個泛型類裡面出現,最好避免在方法的類型參數和類的類型參數中使用同樣的名字來避免混

淆。對內部類也是同樣。

2.1 定義帶類型參數的類

在定義帶類型參數的類時,在緊跟類命之後的<>內,指定一個或多個類型參數的名字,同時也可以對類型參數的取值范圍進行限定,多個類型參數之間用,號分隔。

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

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


2.2 定義待類型參數方法

在定義帶類型參數的方法時,在緊跟可見范圍修飾(例如public)之後的<>內,指定一個或多個類型參數的名字,同時也可以對類型參數的取值范圍進行限定,多個類型參數之間用,號分隔。定義完類型參數後,可以在定義位置之後的方法的任意地方使用類型參數,就像使用普通的類型一樣。


㈤ java中什麼叫泛型

泛型。規定了此集合中元素的類型。例如:

ArrayList<Integer> arr = new ArrayList<Integer> ();

這樣就創建了一個包含整數的 ArrayList 對象。
如果要自己定義泛型類,就用如下形式:

class MyCollection<E> {...}

尖括弧中的類型可以有限制,例如你需要讓 MyCollection 中的類型都具有可比性,可以用如下格式:

class MyCollection<E extends Comparable> {...}

此外,要注意泛型的一些特性:

1. 不能直接創建泛型數組。如 new ArrayList<Integer>[5] 之類的是錯的。只能用如下方法:new ArrayList[5] 或者 (ArrayList<Integer>[])new ArrayList[5];

2. 靜態方法中需要小心,因為 E 一般是非靜態類型,如果你這樣寫:
class MyCollection<E> {
public static MyCollection<E> abc() {
......
}
}
是錯的。你只能把 <E> 去掉。

㈥ 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

㈦ java中泛型是什麼意思,作用是什麼

SytEigyo是一個類的名稱。
sytEigyoList是只包括sytEigyo類類型的實例類對象的列表。
泛型這個東西,說白了就是型參,也就是說類型本身可以作為參數來對類的類型做輔助說明。

㈧ java中泛型指的是什麼

我來簡述一下泛型的知識吧:

如果一個類的後面跟上一個尖括弧,表示這個類是泛型類.

可以這樣聲明:class 名稱<泛型列表>
如:class A<E>
其中A是泛型類的名稱,E是泛型.(可以是任何對象或介面)

其中給出的泛型可以作為類的成員變數的類型,方法的類型以及局部變數的類型.類體和變通類完全一樣,由成員變數和方法構成.

舉個例子:
class Chorus<E,F>
{
void makeChorus(E person,F yueqi)
{
yueqi.toString();
person.toString() ;
}
}
--------------
上面的類中將類E和類F作為類Chorus的一部分來使用.這就是泛型類的目的,將多個類包含進一個類來使用!!!

如果你想深入理解就可以找一些書來看,一些基本的教材裡面也都有提到泛型的.

希望我說的對你有所幫助!!!

㈨ 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中的泛型類是什麼東西

泛型(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 或類文件更改。所有工作都在編譯器中完成,編譯器生成類似於沒有泛型(和強制類型轉換)時所寫的代碼,只是更能確保類型安全而已。
泛型用法的例子

熱點內容
安卓上哪裡下大型游戲 發布:2024-12-23 15:10:58 瀏覽:188
明日之後目前適用於什麼配置 發布:2024-12-23 14:56:09 瀏覽:52
php全形半形 發布:2024-12-23 14:55:17 瀏覽:827
手機上傳助手 發布:2024-12-23 14:55:14 瀏覽:732
什麼樣的主機配置吃雞開全效 發布:2024-12-23 14:55:13 瀏覽:828
安卓我的世界114版本有什麼 發布:2024-12-23 14:42:17 瀏覽:709
vbox源碼 發布:2024-12-23 14:41:32 瀏覽:275
詩經是怎麼存儲 發布:2024-12-23 14:41:29 瀏覽:659
屏蔽視頻廣告腳本 發布:2024-12-23 14:41:24 瀏覽:419
php解析pdf 發布:2024-12-23 14:40:01 瀏覽:818