Java中的泛型方法
泛型是什么意思在這就不多說了,而Java中泛型類的定義也比較簡單,例如:public class Test<T>{}。這樣就定義了一個泛型類Test,在實例化該類時,必須指明泛型T的具體類型,例如:Test<Object> t = new Test<Object>();,指明泛型T的類型為Object。
但是Java中的泛型方法就比較復雜了。
泛型類,是在實例化類的時候指明泛型的具體類型;泛型方法,是在調用方法的時候指明泛型的具體類型。
定義泛型方法語法格式如下:
調用泛型方法語法格式如下:
說明一下,定義泛型方法時,必須在返回值前邊加一個<T>,來聲明這是一個泛型方法,持有一個泛型T,然后才可以用泛型T作為方法的返回值。
Class<T>的作用就是指明泛型的具體類型,而Class<T>類型的變量c,可以用來創建泛型類的對象。
為什么要用變量c來創建對象呢?既然是泛型方法,就代表着我們不知道具體的類型是什么,也不知道構造方法如何,因此沒有辦法去new一個對象,但可以利用變量c的newInstance方法去創建對象,也就是利用反射創建對象。
泛型方法要求的參數是Class<T>類型,而Class.forName()方法的返回值也是Class<T>,因此可以用Class.forName()作為參數。其中,forName()方法中的參數是何種類型,返回的Class<T>就是何種類型。在本例中,forName()方法中傳入的是User類的完整路徑,因此返回的是Class<User>類型的對象,因此調用泛型方法時,變量c的類型就是Class<User>,因此泛型方法中的泛型T就被指明為User,因此變量obj的類型為User。
當然,泛型方法不是僅僅可以有一個參數Class<T>,可以根據需要添加其他參數。
為什么要使用泛型方法呢?因為泛型類要在實例化的時候就指明類型,如果想換一種類型,不得不重新new一次,可能不夠靈活;而泛型方法可以在調用的時候指明類型,更加靈活。
http://wwwiteye.iteye.com/blog/1849917
泛型接口
泛型也可以應用於接口,例如生成器,一種專門負責創建對象的類。這其實是工廠方法設計模式的一種應用。不過使用生成器創建對象時,不需要參數。而工廠方法一般是需要參數的。
- package tik4.generic;
- public interface Generator<T> {
- T next();
- }
一個Fibonacci數列實現
- package tik4.generic;
- public class Fibonacci implements Generator<Integer> {
- private int count;
- // 參數類型用Integer,使用int將不能編譯
- // public int next() {
- // return 0;
- // }
- public Integer next() {
- return fib(count++);
- }
- private int fib(int n) {
- if (n < 2) return 1;
- return fib(n - 2) + fib(n - 1);
- }
- public static void main(String[] args) {
- Fibonacci gen = new Fibonacci();
- for (int i = 0; i <= 17; i++)
- System.out.print(gen.next() + " ");
- }
- /*
- * Output: 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584
- */
- }
泛型方法
可以在類中包含參數化方法,而這個方法所在的類可以是泛型類,也可以不是泛型類。是否擁有泛型方法,和所在的類是否泛型沒有關系。泛型方法使得該方法能夠獨立於類而產生變化。以下是一個基本原則:如果泛型方法可以取代整個類的泛型化,就應該只使用泛型方法。另外,對於一個static方法而言,無法訪問泛型
類的參數類型,所以static方法需要使用泛型能力,就必須成為泛型方法
- package tik4.generic;
- public class GenericMothod {
- public <T,M,N> void getTType(T t,M m,N n){
- /*
- * 傳入int,long ,double等基本類型時,自動打包機制
- * 會將基本類型包裝成相應的對象
- */
- System.out.println(t.getClass().getName());
- System.out.println(m.getClass().getName());
- System.out.println(n.getClass().getName());
- }
- public static void main(String[] args) {
- //泛型類在創建對象時必須指定參數類型,而泛型方法則不需要在創建對象時指定參數類型T
- GenericMothod gm = new GenericMothod();
- gm.getTType("", 1, 1.0);
- gm.getTType(1.0F, 'c', gm);
- }/*
- *Output:
- java.lang.String
- java.lang.Integer
- java.lang.Double
- java.lang.Float
- java.lang.Character
- tik4.generic.GenericMothod
- */
- }
利用參數類型推斷
問題:很煩這種寫法是不是,老子在聲明變量的的時候已經指明了參數類型,為毛還要在初始化對象時再指定?
- Map<Integer, List<? extends Set<String>>> map =
- new HashMap<Integer, List<? extends Set<String>>>();
解決:搞一個工具類
- package tik4.generic;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import java.util.Set;
- public class New {
- public static <K, V> Map<K, V> hashMap() {
- return new HashMap<K, V>();
- }
- public static void main(String[] args) {
- Map<Integer, List<? extends Set<String>>> map = New.hashMap();
- }
- }
注意:
類型推斷只對賦值操作有效,其他時候不起作用。如果你使用泛型方法調用的結果(例如:New.hashMap())作為參數,傳遞給其他方法,此時編譯器不會執行類型推斷。編譯器認為,調用泛型方法之后,其返回值被賦給一個Object類型的變量。上代碼:
- package tik4.generic;
- import java.util.List;
- import java.util.Map;
- import java.util.Set;
- public class LimitsOfInference {
- static void f(Map<Integer, List<? extends Set<String>>> map){};
- public static void main(String[] args) {
- /*
- * the mothed f(Map<Integer, List<? extends Set<String>>>
- * is not applicable for arguments (Map<Object,Object>)
- * 不能編譯
- */
- // f(New.hashMap());
- }
- }
顯示類型說明?? think in java4中是這么寫的,但是我的機器上不能編譯,難道書上是在扯淡
在泛型方法中,可以顯示的指定參數類型。在 點操作符 和 方法名之間插入尖括號,然后將類型置於括號內。如果是在定義該方法的類的內部,則在點操作符之前使用this關鍵字如果使用static方法,必須在點操作符之前加上類名。這種語法,可以解決LimitsOfInference.java中的問題。
- package tik4.generic;
- import java.util.List;
- import java.util.Map;
- import java.util.Set;
- public class ExplicitTypeSpecification {
- static void f(Map<Integer, String> map){};
- public static void main(String[] args) {
- //java 5和java6 中均不能編譯。
- f(New.<Map<Integer, String>>hashMap());
- }
- }
泛型推導在java7中已經實現了。
- List<String> list = new ArrayList<>();
因為編譯器可以從前面(List)推斷出推斷出類型參數,所以后面的ArrayList之后可以不用寫泛型參數了,只用一對空着的尖括號就行。當然,你必須帶着”菱形”<>,否則會有警告的。
Java SE7 只支持有限的類型推斷:只有構造器的參數化類型在上下文中被顯著的聲明了,你才可以使用類型推斷,否則不行。 看代碼:
- List<String> list = new ArrayList<>();
- list.add("A");
- //這個不行
- list.addAll(new ArrayList<>());
- // 這個可以
- List<? extends String> list2 = new ArrayList<>();
- list.addAll(list2);
1.1泛型接口的基本概念


1.2泛型接口實現的兩種方式
interface Info<T>{ // 在接口上定義泛型 public T getVar() ; // 定義抽象方法,抽象方法的返回值就是泛型類型 } class InfoImpl<T> implements Info<T>{ // 定義泛型接口的子類 private T var ; // 定義屬性 public InfoImpl(T var){ // 通過構造方法設置屬性內容 this.setVar(var) ; } public void setVar(T var){ this.var = var ; } public T getVar(){ return this.var ; } }; public class GenericsDemo24{ public static void main(String arsg[]){ Info<String> i = null; // 聲明接口對象 i = new InfoImpl<String>("李興華") ; // 通過子類實例化對象 System.out.println("內容:" + i.getVar()) ; } };如果現在實現接口的子類不想使用泛型聲明,則在實現接口的時候直接指定好其具體的操作類型即可:
interface Info<T>{ // 在接口上定義泛型 public T getVar() ; // 定義抽象方法,抽象方法的返回值就是泛型類型 } class InfoImpl implements Info<String>{ // 定義泛型接口的子類 private String var ; // 定義屬性 public InfoImpl(String var){ // 通過構造方法設置屬性內容 this.setVar(var) ; } public void setVar(String var){ this.var = var ; } public String getVar(){ return this.var ; } }; public class GenericsDemo25{ public static void main(String arsg[]){ Info i = null; // 聲明接口對象 i = new InfoImpl("李興華") ; // 通過子類實例化對象 System.out.println("內容:" + i.getVar()) ; } };
2、泛型方法
2.1定義泛型方法

class Demo{ public <T> T fun(T t){ // 可以接收任意類型的數據 return t ; // 直接把參數返回 } }; public class GenericsDemo26{ public static void main(String args[]){ Demo d = new Demo() ; // 實例化Demo對象 String str = d.fun("李興華") ; // 傳遞字符串 int i = d.fun(30) ; // 傳遞數字,自動裝箱 System.out.println(str) ; // 輸出內容 System.out.println(i) ; // 輸出內容 } };
2.2通過泛型方法返回泛型類的實例
class Info<T extends Number>{ // 指定上限,只能是數字類型 private T var ; // 此類型由外部決定 public T getVar(){ return this.var ; } public void setVar(T var){ this.var = var ; } public String toString(){ // 覆寫Object類中的toString()方法 return this.var.toString() ; } }; public class GenericsDemo27{ public static void main(String args[]){ Info<Integer> i = fun(30) ; System.out.println(i.getVar()) ; } public static <T extends Number> Info<T> fun(T param){ Info<T> temp = new Info<T>() ; // 根據傳入的數據類型實例化Info temp.setVar(param) ; // 將傳遞的內容設置到Info對象的var屬性之中 return temp ; // 返回實例化對象 } };
2.3使用泛型統一傳入參數的類型
class Info<T>{ // 指定上限,只能是數字類型 private T var ; // 此類型由外部決定 public T getVar(){ return this.var ; } public void setVar(T var){ this.var = var ; } public String toString(){ // 覆寫Object類中的toString()方法 return this.var.toString() ; } }; public class GenericsDemo28{ public static void main(String args[]){ Info<String> i1 = new Info<String>() ; Info<String> i2 = new Info<String>() ; i1.setVar("HELLO") ; // 設置內容 i2.setVar("李興華") ; // 設置內容 add(i1,i2) ; } public static <T> void add(Info<T> i1,Info<T> i2){ System.out.println(i1.getVar() + " " + i2.getVar()) ; } };如果add方法中兩個泛型的類型不統一,則編譯會出錯。
class Info<T>{ // 指定上限,只能是數字類型 private T var ; // 此類型由外部決定 public T getVar(){ return this.var ; } public void setVar(T var){ this.var = var ; } public String toString(){ // 覆寫Object類中的toString()方法 return this.var.toString() ; } }; public class GenericsDemo29{ public static void main(String args[]){ Info<Integer> i1 = new Info<Integer>() ; Info<String> i2 = new Info<String>() ; i1.setVar(30) ; // 設置內容 i2.setVar("李興華") ; // 設置內容 add(i1,i2) ; } public static <T> void add(Info<T> i1,Info<T> i2){ System.out.println(i1.getVar() + " " + i2.getVar()) ; } };

3、泛型數組
public class GenericsDemo30{ public static void main(String args[]){ Integer i[] = fun1(1,2,3,4,5,6) ; // 返回泛型數組 fun2(i) ; } public static <T> T[] fun1(T...arg){ // 接收可變參數 return arg ; // 返回泛型數組 } public static <T> void fun2(T param[]){ // 輸出 System.out.print("接收泛型數組:") ; for(T t:param){ System.out.print(t + "、") ; } } };
4、泛型的嵌套設置
class Info<T,V>{ // 接收兩個泛型類型 private T var ; private V value ; public Info(T var,V value){ this.setVar(var) ; this.setValue(value) ; } public void setVar(T var){ this.var = var ; } public void setValue(V value){ this.value = value ; } public T getVar(){ return this.var ; } public V getValue(){ return this.value ; } }; class Demo<S>{ private S info ; public Demo(S info){ this.setInfo(info) ; } public void setInfo(S info){ this.info = info ; } public S getInfo(){ return this.info ; } }; public class GenericsDemo31{ public static void main(String args[]){ Demo<Info<String,Integer>> d = null ; // 將Info作為Demo的泛型類型 Info<String,Integer> i = null ; // Info指定兩個泛型類型 i = new Info<String,Integer>("李興華",30) ; // 實例化Info對象 d = new Demo<Info<String,Integer>>(i) ; // 在Demo類中設置Info類的對象 System.out.println("內容一:" + d.getInfo().getVar()) ; System.out.println("內容二:" + d.getInfo().getValue()) ; } };