所謂泛型:就是允許在定義類別、介面指定類型形參,這個類型形參在將在宣告變數、建立物件時決定(即傳入實際的型別參數,也可稱為型別實參)
泛型類別或介面
「菱形」語法
//定义 public interface List<E> extends Collection<E> public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable //使用 List<String> list = new ArrayList(); //Java7以后可以省略后面尖括号的类型参数 List<String> list = new ArrayList<>();
從泛型類派生子類
//方式1 public class App extends GenericType<String> //方式2 public class App<T> extends GenericType<T> //方式3 public class App extends GenericType
偽泛型
不存在真正的泛型類,泛型類對Java虛擬機來說是透明的. JVM並不知道泛型類別的存在,換句話說,JVM處理泛型類別和普通類別沒什麼區別的.因此在靜態方法、靜態初始化區塊、靜態變數裡面不允許使用類型形參。
- 以下方式都是錯誤的
private static T data; static{ T f; } public static void func(){ T name = 1; }
下面的例子可以從側面驗證不存在泛型類
public static void main(String[] args){ List<String> a1 = new ArrayList<>(); List<Integer> a2 = new ArrayList<>(); System.out.println(a1.getClass() == a2.getClass()); System.out.println(a1.getClass()); System.out.println(a2.getClass()); }
輸出
true class java.util.ArrayList class java.util.ArrayList
類型通配符
首先必須明確,假如FooListBar是一點的父類,但並不是List
以下方法會編譯出錯:
List<?> list = new ArrayList<>(); list.add(new Object());
主意幾點:
1.List
2.陣列和泛型有所不同:假設Foo是Bar的一個子型別(子類別或子介面),那麼Foo[]依然是Bar[]的子型別;但G
3.為了表示各種泛型List的父類,我們需要使用類型通配符,類型通配符是一個問號(?),將一個問號作為類型實參傳給List集合,寫作:List>(意思是未知類型元素的List)。這個問號(?)被稱為通配符,它的元素類型可以匹配任何類型。
通配符的上限
List extends SuperType>表示所有SuperType泛型List的父類或本身。有通配符上限的泛型不能有set方法,只能有get方法。
設定通配符上限能解決如下問題:Dog是Animal子類,有個getSize方法要獲取傳入List的個數,代碼如下
abstract class Animal { public abstract void run(); } class Dog extends Animal { public void run() { System.out.println("Dog run"); } } public class App { public static void getSize(List<Animal> list) { System.out.println(list.size()); } public static void main(String[] args) { List<Dog> list = new ArrayList<>(); getSize(list); // 这里编译报错 } }
這裡編程出錯的原因是List
通配符的下限
List super SubType>表示SubType泛型List的下限。有通配符上限的泛型不能有get方法,只能有set方法。
泛型方法
如果定義類別、介面是沒有使用型別形參,但定義方法時想自己定義型別形參,這也是可以的,JDK1.5也提供了泛型方法的支援。泛型方法的方法簽名比普通方法的方法簽名多了類型形參聲明,類型形參聲明以尖括號括起來,多個類型形參之間以逗號(,)隔開,所有類型形參聲明放在方法修飾符與方法傳回值型別之間.語法格式如下:
修饰符 返回值类型 方法名(类形列表){ //方法体 }
泛型方法允許型別形參被用來表示方法的一個或多個參數之間的型別依賴關係,或是方法傳回值與參數之間的類型依賴關係。如果沒有這樣的類型依賴關係,就不應該使用泛型方法。 Collections的copy方法就使用泛型方法:
public static <T> void copy(List<? super T> dest, List<? extends T> src){ ...}
這個方法要求src型別必須是dest型別的子類別或本身。
擦除和轉換
在嚴格的泛型程式碼裡,帶有泛型聲明的類別總是應該帶著型別參數。但為了與舊的Java程式碼保持一致,也允許在使用帶有泛型聲明的類別時不指定類型參數。如果沒有為這個泛型類別指定型別參數,則該型別參數被稱為一個raw type(原始型別),預設是該宣告該參數時指定的第一個上限型別。
當把一個具有泛型資訊的物件賦給另一個沒有泛型資訊的變數時,則所有在尖括號之間的類型資訊都被丟掉了。比如說一個List
範例
class Apple<T extends Number> { T size; public Apple() { } public Apple(T size) { this.size = size; } public void setSize(T size) { this.size = size; } public T getSize() { return this.size; } } public class ErasureTest { public static void main(String[] args) { Apple<Integer> a = new Apple<>(6); // ① // a的getSize方法返回Integer对象 Integer as = a.getSize(); // 把a对象赋给Apple变量,丢失尖括号里的类型信息 Apple b = a; // ② // b只知道size的类型是Number Number size1 = b.getSize(); // 下面代码引起编译错误 Integer size2 = b.getSize(); // ③ } }
更多Java中的泛型詳解相關文章請關注PHP中文網!