java怎么使用泛型_java泛型 7 泛型的基本介绍和使用
現(xiàn)在開始深入學(xué)習(xí)Java的泛型了,以前一直只是在集合中簡(jiǎn)單的使用泛型,根本就不明白泛型的原理和作用。泛型在java中,是一個(gè)十分重要的特性,所以要好好的研究下。
一、泛型的基本概念
泛型的定義:泛型是JDK 1.5的一項(xiàng)新特性,它的本質(zhì)是參數(shù)化類型(Parameterized Type)的應(yīng)用,也就是說所操作的數(shù)據(jù)類型被指定為一個(gè)參數(shù),在用到的時(shí)候在指定具體的類型。這種參數(shù)類型可以用在類、接口和方法的創(chuàng)建中,分別稱為泛型類、泛型接口和泛型方法。
泛型思想早在C++語言的模板(Templates)中就開始生根發(fā)芽,在Java語言處于還沒有出現(xiàn)泛型的版本時(shí),只能通過Object是所有類型的父類和類型強(qiáng)制轉(zhuǎn)換兩個(gè)特點(diǎn)的配合來實(shí)現(xiàn)類型泛化。例如在哈希表的存取中,JDK 1.5之前使用HashMap的get()方法,返回值就是一個(gè)Object對(duì)象,由于Java語言里面所有的類型都繼承于java.lang.Object,那Object轉(zhuǎn)型為任何對(duì)象成都是有可能的。但是也因?yàn)橛袩o限的可能性,就只有程序員和運(yùn)行期的虛擬機(jī)才知道這個(gè)Object到底是個(gè)什么類型的對(duì)象。在編譯期間,編譯器無法檢查這個(gè)Object的強(qiáng)制轉(zhuǎn)型是否成功,如果僅僅依賴程序員去保障這項(xiàng)操作的正確性,許多ClassCastException的風(fēng)險(xiǎn)就會(huì)被轉(zhuǎn)嫁到程序運(yùn)行期之中。
泛型技術(shù)在C#和Java之中的使用方式看似相同,但實(shí)現(xiàn)上卻有著根本性的分歧,C#里面泛型無論在程序源碼中、編譯后的IL中(Intermediate Language,中間語言,這時(shí)候泛型是一個(gè)占位符)或是運(yùn)行期的CLR中都是切實(shí)存在的,List與List就是兩個(gè)不同的類型,它們?cè)谙到y(tǒng)運(yùn)行期生成,有自己的虛方法表和類型數(shù)據(jù),這種實(shí)現(xiàn)稱為類型膨脹,基于這種方法實(shí)現(xiàn)的泛型被稱為真實(shí)泛型。
Java語言中的泛型則不一樣,它只在程序源碼中存在,在編譯后的字節(jié)碼文件中,就已經(jīng)被替換為原來的原始類型(Raw Type,也稱為裸類型)了,并且在相應(yīng)的地方插入了強(qiáng)制轉(zhuǎn)型代碼,因此對(duì)于運(yùn)行期的Java語言來說,ArrayList與ArrayList就是同一個(gè)類。所以說泛型技術(shù)實(shí)際上是Java語言的一顆語法糖,Java語言中的泛型實(shí)現(xiàn)方法稱為類型擦除,基于這種方法實(shí)現(xiàn)的泛型被稱為偽泛型。(類型擦除在后面在學(xué)習(xí))
使用泛型機(jī)制編寫的程序代碼要比那些雜亂的使用Object變量,然后再進(jìn)行強(qiáng)制類型轉(zhuǎn)換的代碼具有更好的安全性和可讀性。泛型對(duì)于集合類來說尤其有用。
泛型程序設(shè)計(jì)(Generic Programming)意味著編寫的代碼可以被很多不同類型的對(duì)象所重用。
實(shí)例分析:
在JDK1.5之前,Java泛型程序設(shè)計(jì)是用繼承來實(shí)現(xiàn)的。因?yàn)镺bject類是所用類的基類,所以只需要維持一個(gè)Object類型的引用即可。就比如ArrayList只維護(hù)一個(gè)Object引用的數(shù)組:
public?class?ArrayList//JDK1.5之前的
{
public?Object?get(int?i){......}
public?void?add(Object?o){......}
......
private?Object[]?elementData;
}
這樣會(huì)有兩個(gè)問題:
1、沒有錯(cuò)誤檢查,可以向數(shù)組列表中添加類的對(duì)象
2、在取元素的時(shí)候,需要進(jìn)行強(qiáng)制類型轉(zhuǎn)換
這樣,很容易發(fā)生錯(cuò)誤,比如:
/**jdk1.5之前的寫法,容易出問題*/
ArrayList?arrayList1=new?ArrayList();
arrayList1.add(1);
arrayList1.add(1L);
arrayList1.add("asa");
int?i=(Integer)?arrayList1.get(1);//因?yàn)椴恢廊〕鰜淼闹档念愋?#xff0c;類型轉(zhuǎn)換的時(shí)候容易出錯(cuò)
這里的第一個(gè)元素是一個(gè)長(zhǎng)整型,而你以為是整形,所以在強(qiáng)轉(zhuǎn)的時(shí)候發(fā)生了錯(cuò)誤。
所以。在JDK1.5之后,加入了泛型來解決類似的問題。例如在ArrayList中使用泛型:
/**?jdk1.5之后加入泛型*/
ArrayList?arrayList2=new?ArrayList();??//限定數(shù)組列表中的類型
//??????arrayList2.add(1);?//因?yàn)橄薅祟愋?#xff0c;所以不能添加整形
//??????arrayList2.add(1L);//因?yàn)橄薅祟愋?#xff0c;所以不能添加整長(zhǎng)形
arrayList2.add("asa");//只能添加字符串
String?str=arrayList2.get(0);//因?yàn)橹廊〕鰜淼闹档念愋?#xff0c;所以不需要進(jìn)行強(qiáng)制類型轉(zhuǎn)換
還要明白的是,泛型特性是向前兼容的。盡管 JDK 5.0 的標(biāo)準(zhǔn)類庫(kù)中的許多類,比如集合框架,都已經(jīng)泛型化了,但是使用集合類(比如 HashMap 和 ArrayList)的現(xiàn)有代碼可以繼續(xù)不加修改地在 JDK 1.5 中工作。當(dāng)然,沒有利用泛型的現(xiàn)有代碼將不會(huì)贏得泛型的類型安全的好處。
在學(xué)習(xí)泛型之前,簡(jiǎn)單介紹下泛型的一些基本術(shù)語,以ArrayList和ArrayList做簡(jiǎn)要介紹:
整個(gè)成為ArrayList泛型類型
ArrayList中的?E稱為類型變量或者類型參數(shù)
整個(gè)ArrayList?稱為參數(shù)化的類型
ArrayList中的integer稱為類型參數(shù)的實(shí)例或者實(shí)際類型參數(shù)
·ArrayList中的念為typeof?? Integer
ArrayList稱為原始類型
二、泛型的使用
泛型的參數(shù)類型可以用在類、接口和方法的創(chuàng)建中,分別稱為泛型類、泛型接口和泛型方法。下面看看具體是如何定義的。
1、泛型類的定義和使用
一個(gè)泛型類(generic class)就是具有一個(gè)或多個(gè)類型變量的類。定義一個(gè)泛型類十分簡(jiǎn)單,只需要在類名后面加上<>,再在里面加上類型參數(shù):
class?Pair?{
private?T?value;
public?Pair(T?value)?{
this.value=value;
}
public?T?getValue()?{
return?value;
}
public?void?setValue(T?value)?{
this.value?=?value;
}
}
現(xiàn)在我們就可以使用這個(gè)泛型類了:
public?static?void?main(String[]?args)?throws?ClassNotFoundException?{
Pair?pair=new?Pair("Hello");
String?str=pair.getValue();
System.out.println(str);
pair.setValue("World");
str=pair.getValue();
System.out.println(str);
}
Pair類引入了一個(gè)類型變量T,用尖括號(hào)<>括起來,并放在類名的后面。泛型類可以有多個(gè)類型變量。例如,可以定義Pair類,其中第一個(gè)域和第二個(gè)域使用不同的類型:
public class Pair{......}
注意:類型變量使用大寫形式,且比較短,這是很常見的。在Java庫(kù)中,使用變量E表示集合的元素類型,K和V分別表示關(guān)鍵字與值的類型。(需要時(shí)還可以用臨近的字母U和S)表示“任意類型”。
2、泛型接口的定義和使用
定義泛型接口和泛型類差不多,看下面簡(jiǎn)單的例子:
interface?Show{
void?show(T?t,U?u);
}
class?ShowTest?implements?Show{
@Override
public?void?show(String?str,Date?date)?{
System.out.println(str);
System.out.println(date);
}
}
測(cè)試一下:
public?static?void?main(String[]?args)?throws?ClassNotFoundException?{
ShowTest?showTest=new?ShowTest();
showTest.show("Hello",new?Date());
}
3、泛型方法的定義和使用
泛型類在多個(gè)方法簽名間實(shí)施類型約束。在 List 中,類型參數(shù) V 出現(xiàn)在 get()、add()、contains() 等方法的簽名中。當(dāng)創(chuàng)建一個(gè) Map 類型的變量時(shí),您就在方法之間宣稱一個(gè)類型約束。您傳遞給 add() 的值將與 get() 返回的值的類型相同。
類似地,之所以聲明泛型方法,一般是因?yàn)槟胍谠摲椒ǖ亩鄠€(gè)參數(shù)之間宣稱一個(gè)類型約束。
舉個(gè)簡(jiǎn)單的例子:
public?static?void?main(String[]?args)?throws?ClassNotFoundException?{
String?str=get("Hello",?"World");
System.out.println(str);
}
public?static??T?get(T?t,?U?u)?{
if?(u?!=?null)
return?t;
else
return?null;
}
三、泛型變量的類型限定
在上面,我們簡(jiǎn)單的學(xué)習(xí)了泛型類、泛型接口和泛型方法。我們都是直接使用這樣的形式來完成泛型類型的聲明。
有的時(shí)候,類、接口或方法需要對(duì)類型變量加以約束。看下面的例子:
有這樣一個(gè)簡(jiǎn)單的泛型方法:
public?static??T?get(T?t1,T?t2)?{
if(t1.compareTo(t2)>=0);//編譯錯(cuò)誤
return?t1;
}
因?yàn)?#xff0c;在編譯之前,也就是我們還在定義這個(gè)泛型方法的時(shí)候,我們并不知道這個(gè)泛型類型T,到底是什么類型,所以,只能默認(rèn)T為原始類型Object。所以它只能調(diào)用來自于Object的那幾個(gè)方法,而不能調(diào)用compareTo方法。
可我的本意就是要比較t1和t2,怎么辦呢?這個(gè)時(shí)候,就要使用類型限定,對(duì)類型變量T設(shè)置限定(bound)來做到這一點(diǎn)。
我們知道,所有實(shí)現(xiàn)Comparable接口的方法,都會(huì)有compareTo方法。所以,可以對(duì)做如下限定:
public?static??T?get(T?t1,T?t2)?{?//添加類型限定
if(t1.compareTo(t2)>=0);
return?t1;
}
類型限定在泛型類、泛型接口和泛型方法中都可以使用,不過要注意下面幾點(diǎn):
1、不管該限定是類還是接口,統(tǒng)一都使用關(guān)鍵字 extends
2、可以使用&符號(hào)給出多個(gè)限定,比如
public?static??T?get(T?t1,T?t2)
3、如果限定既有接口也有類,那么類必須只有一個(gè),并且放在首位置
public?static??T?get(T?t1,T?t2)
總結(jié)
以上是生活随笔為你收集整理的java怎么使用泛型_java泛型 7 泛型的基本介绍和使用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 对数函数定义域和值域为r_对数函数
- 下一篇: java 德生读卡器对接程序_德生TSW