本文轉載自:http://www.cnblogs.com/lwbqqyumidi/p/3837629.htmlhtml
1 泛型概念的提出(爲何須要泛型?)
java
1.1 首先,咱們看下下面這段簡短的代碼:ide
public class GenericTest { public static void main(String[] args) { List list = new ArrayList(); list.add("qqyumidi"); list.add("corn"); list.add(100); for (int i = 0; i < list.size(); i++) { String name = (String) list.get(i); // 1 System.out.println("name:" + name); } } }
定義了一個List類型的集合,先向其中加入了兩個字符串類型的值,隨後加入一個Integer類型的值。這是徹底容許的,由於此時list默認的類型爲Object類型。在以後的循環中,因爲忘記了以前在list中也加入了Integer類型的值或其餘編碼緣由,很容易出現相似於//1中的錯誤。由於編譯階段正常,而運行時會出現「java.lang.ClassCastException」異常。所以,致使此類錯誤編碼過程當中不易發現。函數
1.2 在上述編碼過程當中,主要存在兩個問題:this
1.3 此問題解決方法:編碼
import java.util.*; public class GenericTesst { public static void main(String[] args) { List list = new ArrayList(); list.add("qqyumidi"); list.add("corn"); list.add(100); for (int i = 0; i < list.size(); i++) { if(list.get(i) instanceof Integer){ String name = ((Integer) list.get(i)).toString(); // 1 System.out.println("name:" + name); }else if(list.get(i) instanceof String){ String name = (String) list.get(i); // 1 System.out.println("name:" + name); } } } }
import java.util.*; public class GenericTesst { public static void main(String[] args) { List list = new ArrayList(); list.add("qqyumidi"); list.add("corn"); list.add(100); for (int i = 0; i < list.size(); i++) { if(list.get(i).getClass().equals(Integer.class)){ String name = ((Integer) list.get(i)).toString(); // 1 System.out.println("name:" + name); }else if(list.get(i).getClass().equals(String.class)){ String name = (String) list.get(i); // 1 System.out.println("name:" + name); } } } }
2 泛型定義(什麼是泛型?)spa
泛型,即參數化類型,相似於函數的形參與實參;在類型定義時將類型定義爲類型形參,而在類型使用時利用類型實參來進行使用。3d
2.1 從源碼中看看List接口是如何定義的code
public interface List<E> extends Collection<E> { int size(); boolean isEmpty(); boolean contains(Object o); Iterator<E> iterator(); Object[] toArray(); <T> T[] toArray(T[] a); boolean add(E e); boolean remove(Object o); boolean containsAll(Collection<?> c); boolean addAll(Collection<? extends E> c); boolean addAll(int index, Collection<? extends E> c); boolean removeAll(Collection<?> c); boolean retainAll(Collection<?> c); void clear(); boolean equals(Object o); int hashCode(); E get(int index); E set(int index, E element); void add(int index, E element); E remove(int index); int indexOf(Object o); int lastIndexOf(Object o); ListIterator<E> listIterator(); ListIterator<E> listIterator(int index); List<E> subList(int fromIndex, int toIndex); }
咱們能夠看到,在List接口中採用泛型化定義以後,<E>中的E表示類型形參,能夠接收具體的類型實參,而且此接口定義中,凡是出現E的地方均表示相同的接受自外部的類型實參。htm
2.2 利用泛型重寫上述代碼
public class GenericTest { public static void main(String[] args) { /* List list = new ArrayList(); list.add("qqyumidi"); list.add("corn"); list.add(100); */ List<String> list = new ArrayList<String>(); list.add("qqyumidi"); list.add("corn"); //list.add(100); // 1 提示編譯錯誤 for (int i = 0; i < list.size(); i++) { String name = list.get(i); // 2 System.out.println("name:" + name); } } }
利用泛型可以使List記錄添加對象的具體類型,從而保證編譯與運行時的一致
3 自定義泛型接口、泛型類及泛型方法
3.1 自定義泛型接口
//定義接口 interface Operatable<T>{ <T> T add(T t1, T t2); }
3.2 自定義泛型類型
//類型的定義,該類型中能夠裝入多種類型的對象 class Box<T>{ private T data ; public Box() { } public Box(T data) { setData(data); } public T getData() { return data; } public void setData(T data) { this.data = data; } }
3.3 自定義泛型方法
static <T> T getData(T t1){ return t1; }
3.3 對於不一樣傳入的類型實參,生成的相應對象實例類型是否是同樣呢?
public class GenericTest { public static void main(String[] args) { Box<String> name = new Box<String>("corn"); Box<Integer> age = new Box<Integer>(712); System.out.println("name class:" + name.getClass()); // com.qqyumidi.Box System.out.println("age class:" + age.getClass()); // com.qqyumidi.Box System.out.println(name.getClass() == age.getClass()); // true } }
由此,說明不一樣傳入的類型實參,並無生成不一樣的類型,傳入不一樣泛型實參的泛型類在內存上只有一個,即仍是原來的最基本的類型。
究其緣由,在於Java中的泛型這一律念提出的目的,致使其只是做用於代碼編譯階段,在編譯過程當中,對於正確檢驗泛型結果後,會將泛型的相關信息擦出,也就是說,成功編譯事後的class文件中是不包含任何泛型信息的。泛型信息不會進入到運行時階段。
對此總結成一句話:泛型類型在邏輯上看以當作是多個不一樣的類型,實際上都是相同的基本類型。
4 類型通配符
4.1 邏輯上Box<Number>不能視爲Box<Integer>的父類
接着上面的結論,咱們知道,Box<Number>和Box<Integer>實際上都是Box類型,如今須要繼續探討一個問題,那麼在邏輯上,相似於Box<Number>和Box<Integer>是否能夠當作具備父子關係的泛型類型呢?
public class GenericTesst { public static void main(String[] args) { Box<Integer> a = new Box<Integer>(712); Box<Number> b = a; // 1 Box<Float> f = new Box<Float>(3.14f); getData(f); // 2 } public static void getData(Box<Number> data) { System.out.println("data :" + data.getData()); } } class Box<T> { private T data; public Box() { } public Box(T data) { setData(data); } public T getData() { return data; } public void setData(T data) { this.data = data; } }
這個例子中,顯然//1和//2處確定會出現錯誤提示的。由此咱們可使用反證法來講明:在邏輯上Box<Number>不能視爲Box<Integer>的父類。
4.2 類型通配符
類型通配符通常是使用 ? 代替具體的類型形參。注意了,此處是類型形參,而不是類型實參!且Box<?>在邏輯上是Box<Integer>、Box<Number>...等全部Box<具體類型實參>的父類。由此,咱們依然能夠定義泛型方法,來完成此類需求。
public class GenericTest { public static void main(String[] args) { Box<String> name = new Box<String>("corn"); Box<Integer> age = new Box<Integer>(712); Box<Number> number = new Box<Number>(314); getData(name); getData(age); getData(number); //getUpperNumberData(name); // 1 getUpperNumberData(age); // 2 getUpperNumberData(number); // 3 } public static void getData(Box<?> data) { System.out.println("data :" + data.getData()); } public static void getUpperNumberData(Box<? extends Number> data){ System.out.println("data :" + data.getData()); } }
類型通配符上限經過形如Box<? extends Number>形式定義,相對應的,類型通配符下限爲Box<? super Number>形式,其含義與類型通配符上限正好相反,