在平常的開發中,咱們會看到別人的框架不少地方會使用到泛型,泛型是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; } }
#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; } }
觀察上面兩個類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; } }
那麼這時候上面的兩個類就能夠經過泛型的設置,相應生成框架
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類的效果。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; }
3.使用泛型類
泛型類的使用代碼以下:spa
ObjClass<String> stringClass = new ObjClass<String>(); stringClass.setX("haha"); ObjClass<Integer> intClass = new ObjClass<Integer>(); intClass.setX(100);
首先,須要構造一個實例:.net
ObjClass<String> stringClass = new ObjClass<String>();
泛型類的構造則須要在類名後添加上,即一對尖括號,中間寫上要傳入的類型。
由於咱們構造時,是這樣的:ObjClass,因此在使用的時候也要在ObjClass後加上類型來定義T表明的意義。code
尖括號中,你傳進去的是什麼,T就表明什麼類型。這就是泛型的最大做用,咱們只須要考慮邏輯實現,就能拿給各類類來用。
1.多泛型變量定義
咱們不止能夠在類中設置一個泛型變量T,還能夠聲明多個泛型變量,寫法以下:
public class ObjClass<T,U>
也就是在原來的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; } }
ObjClass<String,Integer> stringClass = new ObjClass<String,Integer>(); stringClass.setX("haha"); 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; } @Override public void setMsg(T msg) { this.msg = msg; } }
在這個類中,咱們構造了一個泛型類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; } }
咱們不但能夠在類聲明中使用泛型,還能夠在函數聲明中也使用泛型,使用以下:
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()方法定義就合理了,可是使用以上方法也有須要注意的地方,
即:若是使用「?「接收泛型對象的時候,則不能設置被泛型指定的內容。
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) ; } };
若是使用」?「意味着能夠接收任意的內容,可是此內容沒法直接使得用」?「修飾的泛型的對象進行修改。以下就會出問題:
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") ; } };
運行結果:
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)
在使用」?「只能接收,不能修改。
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 + "、") ; } };
運行成功。可是,若是傳入的泛型類型爲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的泛型對象 } };
若是在使用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 + "、") ; } };
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>(); list.add(new Manager()); list.add(new CEO());
在這裏,正由於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());
編譯器沒法肯定<? super Manager>
的具體類型,但惟一能夠肯定的是Manager()、CEO()確定是<? super Manager>
的子類,因此確定是能夠add進去的。但Employee不必定是<? super Manager>
的子類,因此不能肯定,不能肯定的,確定是不容許的,因此會報編譯錯誤。
最後強調一下,List<? super Manager>
list取出的只能是Object 類型,這裏雖然看起來是能取的,但取出來一個Object類型,是毫無心義的。因此纔有了「super通配符:能存不能取」的結論。
總結
1)使用?能夠接收任意泛型對象。
2)泛型的上限:?extends 類型(能取不能存)。
3)泛型的下限:?super 類型? super 通配符(能存不能取)。