1. 程式人生 > >java中的泛型T和?的差異性,學習了

java中的泛型T和?的差異性,學習了

java泛型中T和?有什麼區別?

T 代表一種型別

加在類上:class SuperClass<A>{}

加在方法上:

public <T>void fromArrayToCollection(T[] a, Collection<T> c){}

方法上的<T>代表括號裡面要用到泛型引數,若類中傳了泛型,此處可以不傳,呼叫型別上面的泛型引數,前提是方法中使用的泛型與類中傳來的泛型一致。

class People<T>{

public void show(T a) {

   }

}

T extends T2 指傳的引數為T2或者T2的子型別。

?是萬用字元,泛指所有型別

一般用於定義一個引用變數,這麼做的好處是,如下所示,定義一個sup的引用變數,就可以指向多個物件。

SuperClass<?> sup = new SuperClass<String>("lisi");

sup = new SuperClass<People>(new People());

sup = new SuperClass<Animal>(new Animal());

若不用?,用固定的型別的話,則:

SuperClass<String> sup1 = new SuperClass<String>("lisi");

SuperClass<People> sup2 = new SuperClass<People>("lisi");

SuperClass<Animal> sup3 = new SuperClass<Animal>("lisi");

這就是?萬用字元的好處。

? extends T 指T型別或T的子型別

? super T   指T型別或T的父型別

這個兩個一般也是和?一樣用在定義引用變數中,但是傳值範圍不一樣

T和?運用的地方有點不同,?是定義在引用變數上,T是類上或方法上

如果有泛型方法和非泛型方法,都滿足條件,會執行非泛型方法

public void show(String s){

      System.out.println("1");

   }

   @Override

   public void show(T a) {

      System.out.println("2");

   }

1.在整個類中只有一處使用了泛型,使用時注意加了泛型了引數不能呼叫與引數型別有關的方法比如“+”,比如打印出任意引數化型別集合中的所有內容,就適合用萬用字元泛型<?>
public static void printCollecton(Collection <?> collection)
{
for(Object obj: collection)
{
System.out.println(obj);
}
}


2. 當一個型別變數用來表達兩個引數之間或者引數與返回值之間的關係時,即統一各型別變數在方法簽名的兩處被使用,或者型別變數在方法體程式碼中也被使用而不僅僅在簽名的時候使用,這是應該用自定義泛型<T>。泛型方可以呼叫一些時間型別的方法。比如集合的add方法。
public static <T> T autoConvertType(T obj)
{
     return(T)obj;
}

泛型三種:
          [1]ArrayList<T> al=new ArrayList<T>();指定集合元素只能是T型別
          [2]ArrayList<?> al=new ArrayList<?>();集合元素可以是任意型別,這種沒有意義,一般是方法中,只是為了說明用法
          [3]ArrayList<? extends E> al=new ArrayList<? extends E>();
            泛型的限定:
               ? extends E:接收E型別或者E的子型別。
               ?super E:接收E型別或者E的父型別。

java泛型的兩種用法:List<T>是泛型方法,List<?>是限制萬用字元


List<T>一般有兩種用途:
1、定義一個通用的泛型方法。
public interface Dao{
  List<T> getList(){};
}
 
List<String> getStringList(){
  return dao.getList();//dao是一個實現類例項
}
 
List<Integer> getIntList(){
  return dao.getList();
}
上面介面的getList方法如果定義成List<?> ,後面就會報錯。

2、限制方法的引數之間或引數和返回結果之間的關係。
List<T> getList<T param1,T param2>
這樣可以限制返回結果的型別以及兩個引數的型別一致。

List<?>一般就是在泛型起一個限制作用。
public Class Fruit(){}

public Class Apple extends Fruit(){}

public void test(? extends Fruit){};

test(new Fruit());
test(new Apple());
test(new String()); //這個就會報錯,
引數必須是Fruit或其子類。

“<T>"和"<?>",首先要區分開兩種不同的場景:


第一,宣告一個泛型類或泛型方法。
第二,使用泛型類或泛型方法。
型別引數“<T>”主要用於第一種,宣告泛型類或泛型方法。
無界萬用字元“<?>”主要用於第二種,使用泛型類或泛型方法

再來看一下具體概念:

泛型介面:

定義一個泛型介面:

通過類去實現這個泛型介面的時候指定泛型T的具體型別。

指定具體型別為Integer:

指定具體型別為String:

指定具體型別為一個自定義的物件:

泛型類:

在編譯器,是無法知道K和V具體是什麼型別,只有在執行時才會真正根據型別來構造和分配記憶體。

泛型方法:

引用其他人寫的,覺得不錯:

定義泛型方法時,必須在返回值前邊加一個<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一次,可能不夠靈活;而泛型方法可以在呼叫的時候指明型別,更加靈活。

總結:泛型的使用使我們的程式碼更加具有通用性,不會導致定義了一種型別之後其他的型別都無法使用該程式碼。

   通過泛型可以定義型別安全的資料結構(型別安全),而無須使用實際的資料型別(可擴充套件)。這能夠顯著提高效能並得到更高質量的程式碼(高效能),因為您可以重用資料處理演算法,而無須複製型別特定的程式碼(可重用)