- 泛型的解釋
現在感覺泛型是一個值得學習的地方,就抽出時間來學習和總結一下泛型的使用。
Java 泛型(generics)是 JDK 5 中引入的一個新特性, 泛型提供了編譯時類型安全檢測機制,該機制允許程序員在編譯時檢測到非法的類型。
泛型的本質是參數化類型,也就是說所操作的數據類型被指定為一個參數。
泛型是一種把類型的明確工作推遲到創建對象或者調用方法的時候才去明確的特殊類型。
注意:類型參數只能代表引用型類型,不能是原始類型(像int,double,char的等)。
- 泛型出現的原因
早期的時候,使用Object來代表任意類型。但是這樣在向上轉型的是沒有問題的,但是在向下轉型的時候存在類型轉換的問題,這樣的程序其實是不安全的。所以Java在JDK5之后提供了泛型來解決這個問題。
使用泛型的典型例子,是在集合中的泛型使用。
在使用泛型前,存入集合中的元素可以是任何類型的,當從集合中取出時,所有的元素都是Object類型,需要進行向下的強制類型轉換,轉換到特定的類型。
泛型的思想就是由程序員指定類型,這樣集合就只能容納該類型的元素。
- 實例分析
在JDK1.5之前,Java泛型程序設計是用繼承來實現的。因為Object類是所用類的基類,所以只需要維持一個Object類型的引用即可。就比如ArrayList只維護一個Object引用的數組:
public class ArrayList//JDK1.5之前的 { public Object get(int i){......} public void add(Object o){......} ...... private Object[] elementData; }
- 這樣會有兩個問題:
1、沒有錯誤檢查,可以向數組列表中添加類的對象
2、在取元素的時候,需要進行強制類型轉換。這樣,很容易發生錯誤,比如:
/**jdk1.5之前的寫法,容易出問題*/ ArrayList arrayList1=new ArrayList(); arrayList1.add(1); arrayList1.add(1L); arrayList1.add("asa"); int i=(Integer) arrayList1.get(1);//因為不知道取出來的值的類型,類型轉換的時候容易出錯
- 這里的第一個元素是一個長整型,而你以為是整形,所以在強轉的時候發生了錯誤。
所以。在JDK1.5之后,加入了泛型來解決類似的問題。例如在ArrayList中使用泛型:
/** jdk1.5之后加入泛型*/ ArrayList<String> arrayList2=new ArrayList<String>(); //限定數組列表中的類型 // arrayList2.add(1); //因為限定了類型,所以不能添加整形 // arrayList2.add(1L);//因為限定了類型,所以不能添加整長形 arrayList2.add("asa");//只能添加字符串 String str=arrayList2.get(0);//因為知道取出來的值的類型,所以不需要進行強制類型轉換
還要明白的是,泛型特性是向前兼容的。盡管 JDK 5.0 的標准類庫中的許多類,比如集合框架,都已經泛型化了,但是使用集合類(比如 HashMap 和 ArrayList)的現有代碼可以繼續不加修改地在 JDK 1.5 中工作。當然,沒有利用泛型的現有代碼將不會贏得泛型的類型安全的好處。
一、泛型類(注意:一旦在類上聲明泛型,在類里面所有非靜態成員上都可以使用)
格式:public class 類名<參數類型,...>{} 當然這里<>里面也可以使用多個不同參數類型例如:public class User<T,E>{}
注意:類型變量使用大寫形式,且比較短,這是很常見的。在Java庫中,使用變量E表示集合的元素類型,K和V分別表示關鍵字與值的類型。(需要時還可以用臨近的字母U和S)表示“任意類型”。
public class ObjectTool<T> { private T obj; public void setObj(T obj) { this.obj = obj; } public T getObj() { return obj; } } public static void main(String[] args) { ObjectTool<String> obj = new ObjectTool<>(); obj.setObj("Hello World."); System.out.println(obj.getObj()); }
二、泛型方法
格式:public <T> 返回值 方法名(T a){}
public class ObjectTool { public <E> void show(E s) { System.out.println(s); } }
public <E> void show(E s) { System.out.println(s); } public static void main(String[] args) { ObjectTool obj = new ObjectTool(); obj.show("Hello world.");
obj.show(120); }
- 方法上的泛型和類上的泛型很像,唯一不同的是類型的作用域不同
三、泛型通配符
<?>:類型通配符一般是使用?代替具體的類型參數。例如 List<?> 在邏輯上是List<String>,List<Integer> 等所有List<具體類型實參>的父類。
//泛型如果明確的寫的時候,前后類型必須一致 Collection<Object> c1 = new ArrayList<Object>(); Collection<Object> c2 = new ArrayList<Animal>(); //報錯 Collection<Object> c3 = new ArrayList<Dog>(); //報錯 //?表示任意類型都是可以的 Collection<?> c4 = new ArrayList<Object>(); Collection<?> c5 = new ArrayList<Animal>(); Collection<?> c6 = new ArrayList<Dog>();
<? extends E>:向下限定,只能是E及其子類
Collection<? extends Animal> c1 = new ArrayList<Object>(); //報錯 Collection<? extends Animal> c2 = new ArrayList<Animal>(); Collection<? extends Animal> c3 = new ArrayList<Dog>(); Collection<? extends Animal> c4 = new ArrayList<Cat>();
<? super E>:向上限定,只能是E及其父類
Collection<? super Animal> c1 = new ArrayList<Object>(); Collection<? super Animal> c2 = new ArrayList<Animal>(); Collection<? super Animal> c3 = new ArrayList<Dog>(); //報錯 Collection<? super Animal> c4 = new ArrayList<Cat>(); //報錯
四、繼承泛型類別
父類:
public class Parent<T1,T2> { private T1 foo1; private T2 foo2; public T1 getFoo1() { return foo1; } public void setFoo1(T1 foo1) { this.foo1 = foo1; } public T2 getFoo2() { return foo2; } public void setFoo2(T2 foo2) { this.foo2 = foo2; } }
子類:
public class Child<T1, T2, T3> extends Parent<T1, T2> { private T3 foo3; public T3 getFoo3() { return foo3; } public void setFoo3(T3 foo3) { this.foo3 = foo3; } }
五、實現泛型接口
泛型接口
public interface ParentInterface<T1,T2> { public void setFoo1(T1 foo1); public void setFoo2(T2 foo2); public T1 getFoo1(); public T2 getFoo2(); }
子類實現泛型接口:
public class ChildClass<T1,T2> implements ParentInterface<T1, T2> { private T1 foo1; private T2 foo2; @Override public void setFoo1(T1 foo1) { this.foo1 = foo1; } @Override public void setFoo2(T2 foo2) { this.foo2 = foo2; } @Override public T1 getFoo1() { return this.foo1; } @Override public T2 getFoo2() { return this.foo2; } }
還可以在實現接口的時候定義類型:
interface Show<T,U>{ void show(T t,U u); } class ShowTest implements Show<String,Date>{ @Override public void show(String str,Date date) { System.out.println(str); System.out.println(date); } }
測試一下:
public static void main(String[] args) throws ClassNotFoundException { ShowTest showTest=new ShowTest(); showTest.show("Hello",new Date()); }
-
不允許使用泛型的地方
public class GenericsExample<T> { private static T member; //This is not allowed }
public class GenericsExample<T> { public GenericsExample(){ new T(); } }
final List<int> ids = new ArrayList<>(); //不允許 final List<Integer> ids = new ArrayList<>(); //允許
// 引起編譯錯誤 public class GenericException<T> extends Exception {}