java泛型詳解

       在平常的開發中,咱們會看到別人的框架不少地方會使用到泛型,泛型是Java SE 1.5的新特性,泛型的本質是參數化類型,也就是說所操做的數據類型被指定爲一個參數。這種參數類型能夠用在類、接口和方法的建立中,分別稱爲泛型類、泛型接口、泛型方法。泛型的類型參數只能是類類型(包括自定義類),不能是簡單類型。本篇博客咱們就來詳細解析一下泛型的知識。java

泛型類定義及使用

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

#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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
#IntClass 
public class IntClass {
    private int x ;
    private int y ;

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

觀察上面兩個類StringClass 和IntClass,他們除了變量類型不同,一個是String一個是int之外,其它並無什麼區別!那咱們能不能合併成一個呢?經過泛型就能夠解決,首先看一下泛型的類是怎麼定義的:app

public class ObjClass<T> {

    private T x ;
    private T y ;

    public T getX() {
        return x;
    }

    public void setX(T x) {
        this.x = x;
    }

    public T getY() {
        return y;
    }

    public void setY(T y) {
        this.y = y;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

那麼這時候上面的兩個類就能夠經過泛型的設置,相應生成框架

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());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

這裏寫圖片描述
從結果中能夠看到,咱們經過泛型實現了開篇中StringClass類和IntClass類的效果。ide

接下來介紹泛型如何定義及使用:
1.首先須要定義泛型:ObjClass
ObjClass ,即在類名後面加一個尖括號,括號裏是一個大寫字母。這裏寫的是T,其實這個字母能夠是任何大寫字母,不管使用哪一個字母,意義都是相同的。函數

2.在類中使用泛型
這個T表示派生自Object類的任何類,好比String,Integer,Double等等。這裏要注意的是,T必定是派生於Object類的。this

private T x ;
    private T y ;

    public T getX() {
        return x;
    }

    public void setX(T x) {
        this.x = x;
    }

    public T getY() {
        return y;
    }

    public void setY(T y) {
        this.y = y;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

3.使用泛型類
泛型類的使用代碼以下:spa

ObjClass<String> stringClass = new ObjClass<String>();
        stringClass.setX("haha");

        ObjClass<Integer> intClass = new ObjClass<Integer>();
        intClass.setX(100);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

首先,須要構造一個實例:.net

ObjClass<String> stringClass = new ObjClass<String>();
  • 1
  • 1

泛型類的構造則須要在類名後添加上,即一對尖括號,中間寫上要傳入的類型。
由於咱們構造時,是這樣的:ObjClass,因此在使用的時候也要在ObjClass後加上類型來定義T表明的意義。code

尖括號中,你傳進去的是什麼,T就表明什麼類型。這就是泛型的最大做用,咱們只須要考慮邏輯實現,就能拿給各類類來用。

多泛型變量定義

1.多泛型變量定義
咱們不止能夠在類中設置一個泛型變量T,還能夠聲明多個泛型變量,寫法以下:

public class ObjClass<T,U>
  • 1
  • 1

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

public class ObjClass<T,U> {

    private T x ;
    private U y ;

    public T getX() {
        return x;
    }

    public void setX(T x) {
        this.x = x;
    }

    public U getY() {
        return y;
    }

    public void setY(U y) {
        this.y = y;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
ObjClass<String,Integer> stringClass = new ObjClass<String,Integer>();
        stringClass.setX("haha");
        stringClass.setY(100);
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

從上面的代碼中,能夠明顯看出,就是在新添加的泛型變量U用法與T是同樣的。

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

E — Element,經常使用在java Collection裏,如:  List<E>,Iterator<E>,Set<E>
 K,V — Key,Value,表明Map的鍵值對
 N — Number,數字
 T — Type,類型,如String,Integer等等
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

泛型接口定義及使用

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

interface MsgClass<T> {
    public T getMsg() ;
    public void setMsg(T x);
}
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

咱們能夠利用泛型類來構造填充泛型接口

public class Message<T,U> implements MsgClass<T>{

    private T msg;
    @Override
    public T getMsg() {
        return msg;
    }

    @Override
    public void setMsg(T msg) {
        this.msg = msg;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在這個類中,咱們構造了一個泛型類Message,而後把泛型變量T傳給了MsgClass,這說明接口和泛型類使用的都是同一個泛型變量。

咱們還能夠構造一個多個泛型變量的類,並繼承自MsgClass接口:

public class Message<T,U> implements MsgClass<T>{
    private U name;
    private T msg;
    @Override
    public T getMsg() {
        return msg;
    }

    @Override
    public void setMsg(T msg) {
        this.msg = msg;
    }

    public U getName() {
        return name;
    }

    public void setName(U name) {
        this.name = name;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

泛型函數定義及使用

咱們不但能夠在類聲明中使用泛型,還能夠在函數聲明中也使用泛型,使用以下:

public class ObjClass {
    //靜態函數
    public static <T> void StaticMethod(T a) {

    }

    //普通函數
    public <T> void OrgnicMethod(T a) {

    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

上面分別是靜態泛型函數和常規泛型函數的定義方法,與以往方法的惟一不一樣點就是在返回值前加上來表示泛型變量。

不管哪一種泛型方法都有兩種使用方法:

//靜態方法
ObjClass.StaticMethod("adfdsa");//使用方法一
ObjClass.<String>StaticMethod("adfdsa");//使用方法二
//常規方法
ObjClass objClass = new ObjClass();
objClass.OrgnicMethod(new Integer(111));//使用方法一
objClass.<Integer>OrgnicMethod(new Integer(111));//使用方法二
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

方法一,隱式傳遞了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;  
}
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

函數返回值是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) ;
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

定義了一個靜態函數,而後定義返回值爲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);
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

編譯發生錯誤。

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)
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

泛型對象進行引用傳遞的時候,類型必須一致,若是非要傳遞,則能夠將fun方法中Info參數的泛型取消掉(變成 void fun(Info temp))。、

以上確實改進了功能,可是彷佛不是很穩當,畢竟以前指定過泛型。

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

即:若是使用「?「接收泛型對象的時候,則不能設置被泛型指定的內容。

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 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) ;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

若是使用」?「意味着能夠接收任意的內容,可是此內容沒法直接使得用」?「修飾的泛型的對象進行修改。以下就會出問題:

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[]){
        Info<?> i = new Info<String>() ;       
        i.setVar("ABCD") ;                            
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

運行結果:

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
    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)
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

在使用」?「只能接收,不能修改。

泛型的上限

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 GenericsDemo{
    public static void main(String args[]){
        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 + "、") ;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

運行成功。可是,若是傳入的泛型類型爲String的話就不行,由於String不是Number子類。
在類中使用泛型上限。

class Info<T extends Number>{    // 此處泛型只能是數字類型
    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[]){
        Info<Integer> i1 = new Info<Integer>() ;        // 聲明Integer的泛型對象
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

若是在使用Info的時候設置成String類型,則編譯的時候將會出現錯誤(String不是Number子類)
注意:利用<? extends Number>定義的變量,只可取其中的值,不可修改

緣由以下:
由於Info的類型爲 Info

泛型的下限

<? super XXX>表示填充爲任意XXX的父類

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 GenericsDemo21{
    public static void main(String args[]){
        Info<String> i1 = new Info<String>() ;        // 
        Info<Object> i2 = new Info<Object>() ;        // 
        i1.setVar("hello") ;
        i2.setVar(new Object()) ;
        fun(i1) ;
        fun(i2) ;
    }
    public static void fun(Info<? super String> temp){    // 只能接收String或Object類型的泛型,String類的父類只有Object類
        System.out.print(temp + "、") ;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

Object類和String類都是String的父類,全部運行成功,可是若是此時用Integer則會出錯,由於integer並非String父類。

注意:使用super通配符:能存不能取
如何理解呢?假設有3個類,繼承關係以下:

class CEO extends Manager {  
}  

class Manager extends Employee {  
}  

class Employee {  
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

而後書寫以下代碼:

List<? super Manager> list;  
list = new ArrayList<Employee>();  
//存  
list.add(new Employee()); //編譯錯誤  
list.add(new Manager());  
list.add(new CEO());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

爲何而list.add(new Employee());是錯誤的?
由於list裏item的類型是

List<Employee> list = new ArrayList<Employee>();  
list.add(new Manager());  
list.add(new CEO());
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

在這裏,正由於Manager和CEO都是Employee的子類,在傳進去list.add()後,會被強制轉換爲Employee!
如今回過頭來看這個:

List<? super Manager> list;  
list = new ArrayList<Employee>();  
//存  
list.add(new Employee()); //編譯錯誤  
list.add(new Manager());  
list.add(new CEO());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

編譯器沒法肯定<? super Manager>的具體類型,但惟一能夠肯定的是Manager()、CEO()確定是<? super Manager>的子類,因此確定是能夠add進去的。但Employee不必定是<? super Manager>的子類,因此不能肯定,不能肯定的,確定是不容許的,因此會報編譯錯誤。

最後強調一下,List<? super Manager> list取出的只能是Object 類型,這裏雖然看起來是能取的,但取出來一個Object類型,是毫無心義的。因此纔有了「super通配符:能存不能取」的結論。

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

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

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

相關文章
相關標籤/搜索