天天看點

Java 泛型詳解

在日常的開發中,我們會看到别人的架構很多地方會使用到泛型,泛型是java se 1.5的新特性,泛型的本質是參數化類型,也就是說所操作的資料類型被指定為一個參數。這種參數類型可以用在類、接口和方法的建立中,分别稱為泛型類、泛型接口、泛型方法。泛型的類型參數隻能是類類型(包括自定義類),不能是簡單類型。本篇部落格我們就來詳細解析一下泛型的知識。

泛型類定義及使用

使用泛型有什麼好處呢?首先我們先看一個例子,假設我們有兩個類,代碼如下:

#stringclass 

public class stringclass {

    private string x ;

    private string y ;

    public string gety() {

        return y;

    }

    public void sety(string y) {

        this.y = y;

    public string getx() {

        return x;

    public void setx(string x) {

        this.x = x;

}

#intclass 

public class intclass {

    private int x ;

    private int y ;

    public int gety() {

    public void sety(int y) {

    public int getx() {

    public void setx(int x) {

觀察上面兩個類stringclass 和intclass,他們除了變量類型不一樣,一個是string一個是int以外,其它并沒有什麼差別!那我們能不能合并成一個呢?通過泛型就可以解決,首先看一下泛型的類是怎麼定義的:

public class objclass<t> {

    private t x ;

    private t y ;

    public t getx() {

    public void setx(t x) {

    public t gety() {

    public void sety(t y) {

那麼這時候上面的兩個類就可以通過泛型的設定,相應生成

objclass<string> stringclass = new objclass<string>();

        stringclass.setx("haha");

        objclass<integer> intclass = new objclass<integer>();

        intclass.setx(100);

        log.d("yyy", "stringclass:" + stringclass.getx() + ",intclass:" + intclass.getx());

從結果中可以看到,我們通過泛型實作了開篇中stringclass類和intclass類的效果。

接下來介紹泛型如何定義及使用:

1.首先需要定義泛型:objclass

objclass ,即在類名後面加一個尖括号,括号裡是一個大寫字母。這裡寫的是t,其實這個字母可以是任何大寫字母,無論使用哪個字母,意義都是相同的。如果你想學習java可以來這個群,首先是二二零,中間是一四二,最後是九零六,裡面有大量的學習資料可以下載下傳

2.在類中使用泛型

這個t表示派生自object類的任何類,比如string,integer,double等等。這裡要注意的是,t一定是派生于object類的。

private t x ;

3.使用泛型類

泛型類的使用代碼如下:

首先,需要構造一個執行個體:

泛型類的構造則需要在類名後添加上,即一對尖括号,中間寫上要傳入的類型。

因為我們構造時,是這樣的:objclass,是以在使用的時候也要在objclass後加上類型來定義t代表的意義。

尖括号中,你傳進去的是什麼,t就代表什麼類型。這就是泛型的最大作用,我們隻需要考慮邏輯實作,就能拿給各種類來用。

多泛型變量定義

1.多泛型變量定義

我們不止可以在類中設定一個泛型變量t,還可以聲明多個泛型變量,寫法如下:

public class objclass<t,u>

也就是在原來的t後面用逗号隔開,寫上其它的任意大寫字母即可,如果還有多個,依然使用逗号分隔開即可,則我們前面定義的泛型類就會變成下面這樣:

public class objclass<t,u> {

    private u y ;

    public u gety() {

    public void sety(u y) {

objclass<string,integer> stringclass = new objclass<string,integer>();

        stringclass.sety(100);

從上面的代碼中,可以明顯看出,就是在新添加的泛型變量u用法與t是一樣的。

2.泛型的字母規範

雖然在類中聲明泛型任意字母都可以,但為了可讀性,最好遵循以下的規範:

e — element,常用在java collection裡,如:  list<e>,iterator<e>,set<e>

 k,v — key,value,代表map的鍵值對

 n — number,數字

 t — type,類型,如string,integer等等

泛型接口定義及使用

在接口上定義泛型與在類中定義泛型是一樣的,代碼如下:

interface msgclass<t> {

    public t getmsg() ;

    public void setmsg(t x);

我們可以利用泛型類來構造填充泛型接口

public class message<t,u> implements msgclass<t>{

    private t msg;

    @override

    public t getmsg() {

        return msg;

    public void setmsg(t msg) {

        this.msg = msg;

在這個類中,我們構造了一個泛型類message,然後把泛型變量t傳給了msgclass,這說明接口和泛型類使用的都是同一個泛型變量。

我們還可以構造一個多個泛型變量的類,并繼承自msgclass接口:

    private u name;

    public u getname() {

        return name;

    public void setname(u name) {

        this.name = name;

泛型函數定義及使用

我們不但可以在類聲明中使用泛型,還可以在函數聲明中也使用泛型,使用如下:

public class objclass {

    //靜态函數

    public static <t> void staticmethod(t a) {

    //普通函數

    public <t> void orgnicmethod(t a) {

上面分别是靜态泛型函數和正常泛型函數的定義方法,與以往方法的唯一不同點就是在傳回值前加上來表示泛型變量。

無論哪種泛型方法都有兩種使用方法:

//靜态方法

objclass.staticmethod("adfdsa");//使用方法一

objclass.<string>staticmethod("adfdsa");//使用方法二

//正常方法

objclass objclass = new objclass();

objclass.orgnicmethod(new integer(111));//使用方法一

objclass.<integer>orgnicmethod(new integer(111));//使用方法二

方法一,隐式傳遞了t的類型,這種隐式的傳遞方式,代碼不利于閱讀和維護。因為從外觀根本看不出來你調用的是一個泛型函數。

方法二,例如上面例子中,将t指派為integer類型,這樣orgnicmethod(t a)傳遞過來的參數如果不是integer那麼編譯器就會報錯。

當然泛型函數的傳回值也可以使用泛型表示:

public static <t> list<t> parsearray(string response,class<t> object){  

    list<t> modellist = json.parsearray(response, object);  

    return modellist;  

函數傳回值是list類型。和void的泛型函數不同,有傳回值的泛型函數要在函數定義的中在傳回值前加上辨別泛型;還要說明的是,上面中,使用class傳遞泛型類class對象

泛型數組

泛型同樣可以用來定義在數組上

//定義  

        public static <t> t[] fun1(t...msg){  // 接收可變參數    

            return msg ;            // 傳回泛型數組    

        }

        //使用  

        public static void main(string args[]){

            integer i[] = fun1(8,9,8,44) ;

            integer[] result = fun1(i) ;

定義了一個靜态函數,然後定義傳回值為t[],參數為接收的t類型的可變長參數。

泛型的通配符

在開發中對象的引用傳遞(向上向下傳遞)是最常見的,但是,在泛型的操作中,在進行引用傳遞的時候泛型類型必須比對才可以傳遞,否則不能傳遞。

例如,如下沒有進行泛型類型比對,一個是string,一個是object類型。

class info<t>{

    private t var ;        // 定義泛型變量

    public void setvar(t var){

        this.var = var ;

    public t getvar(){

        return this.var ;

    public string tostring(){   

        return this.var.tostring() ;

};

public class demo1 {

        public static void main(string args[]) {

            // 使用string為泛型類型

            info<string> i = new info<string>();        

            i.setvar("abcd");

            //把string泛型類型的i對象傳遞給object泛型類型的temp。

            fun(i);                   

        // 接收object泛型類型的info對象

        public static void fun(info<object> temp) {        

            system.out.println("内容:" + temp);

編譯發生錯誤。

exception in thread "main" java.lang.error: unresolved compilation problem: 

    the method fun(info<object>) in the type demo1 is not applicable for the arguments (info<string>)

    at thread1.demo1.main(demo1.java:18)

泛型對象進行引用傳遞的時候,類型必須一緻,如果非要傳遞,則可以将fun方法中info參數的泛型取消掉(變成 void fun(info temp))。、

以上确實改進了功能,但是似乎不是很妥當,畢竟之前指定過泛型。

以上程式在fun()方法中使用 "info<?>" 的代碼形式,表示可以使用任意的泛型類型對象,這樣的話fun()方法定義就合理了,但是使用以上方法也有需要注意的地方,

即:如果使用“?“接收泛型對象的時候,則不能設定被泛型指定的内容。

    private t var ;        

    public string tostring(){    

public class genericsdemo{

    public static void main(string args[]){

        info<string> i = new info<string>() ;       

        i.setvar("abcd") ;                            

        fun(i) ;

    public static void fun(info<?> temp){        

        system.out.println("内容:" + temp) ;

如果使用”?“意味着可以接收任意的内容,但是此内容無法直接使得用”?“修飾的泛型的對象進行修改。如下就會出問題:

public class demo1{

        info<?> i = new info<string>() ;       

運作結果:

    the method setvar(capture#1-of ?) in the type info<capture#1-of ?> is not applicable for the arguments (string)

    at thread1.demo1.main(demo1.java:17)

在使用”?“隻能接收,不能修改。

泛型的上限

        info<integer> i1 = new info<integer>() ;        

        info<float> i2 = new info<float>() ;            

        i1.setvar(30) ;                                    

        i2.setvar(30.1f) ;                                

        fun(i1) ;

        fun(i2) ;

    public static void fun(info<? extends number> temp){    // 隻能接收number及其number的子類

        system.out.print(temp + "、") ;

運作成功。但是,如果傳入的泛型類型為string的話就不行,因為string不是number子類。

在類中使用泛型上限。

class info<t extends number>{    // 此處泛型隻能是數字類型

        info<integer> i1 = new info<integer>() ;        // 聲明integer的泛型對象

如果在使用info的時候設定成string類型,則編譯的時候将會出現錯誤(string不是number子類)

注意:利用 <? extends number> 定義的變量,隻可取其中的值,不可修改

原因如下:

因為info的類型為 info

泛型的下限

<? super xxx> 表示填充為任意xxx的父類

public class genericsdemo21{

        info<string> i1 = new info<string>() ;        // 

        info<object> i2 = new info<object>() ;        // 

        i1.setvar("hello") ;

        i2.setvar(new object()) ;

    public static void fun(info<? super string> temp){    // 隻能接收string或object類型的泛型,string類的父類隻有object類

object類和string類都是string的父類,所有運作成功,但是如果此時用integer則會出錯,因為integer并不是string父類。

注意:使用super通配符:能存不能取

如何了解呢?假設有3個類,繼承關系如下:

class ceo extends manager {  

}  

class manager extends employee {  

class employee {  

然後書寫如下代碼:

list<? super manager> list;  

list = new arraylist<employee>();  

//存  

list.add(new employee()); //編譯錯誤  

list.add(new manager());  

list.add(new ceo());

為什麼而list.add(new employee());是錯誤的?

因為list裡item的類型是

list<employee> list = new arraylist<employee>();  

在這裡,正因為manager和ceo都是employee的子類,在傳進去list.add()後,會被強制轉換為employee!

現在回過頭來看這個:

編譯器無法确定 <? super manager> 的具體類型,但唯一可以确定的是manager()、ceo()肯定是 <? super manager> 的子類,是以肯定是可以add進去的。但employee不一定是 <? super manager> 的子類,是以不能确定,不能确定的,肯定是不允許的,是以會報編譯錯誤。

最後強調一下, list<? super manager> list取出的隻能是object 類型,這裡雖然看起來是能取的,但取出來一個object類型,是毫無意義的。是以才有了“super通配符:能存不能取”的結論。

總結1)使用?可以接收任意泛型對象。

2)泛型的上限:?extends 類型(能取不能存)。

3)泛型的下限:?super 類型? super 通配符(能存不能取)。