看成筆記整理的~~~ 大量轉載自:http://www.cnblogs.com/lwbqqyumidi/p/3837629.htmlhtml
首先,引出堆對象這個概念。java
什麼是堆對象,就是程序在運行過程當中能夠隨時創建或者刪除的對象,能夠用new運算符(或malloc函數)或者delete運算符(或free函數)。泛型能夠看做是一類堆對象。程序員
泛型是程序設計語言的一種特性。容許程序員在強類型程序設計語言中編寫代碼時定義一些可變部分,那些部分在使用前必須做出指明。數組
各類程序設計語言和其編譯器、運行環境對泛型的支持均不同。將類型參數化以達到代碼複用提升軟件開發工做效率的一種數據類型。app
泛型的定義主要有兩種:
1.在程序編碼中一些包含類型參數的類型,也就是說泛型的參數只能夠表明類,不能表明個別對象。(這是當今較常見的定義)ide
2.在程序編碼中一些包含參數的類。其參數能夠表明類或對象等等。(人們大多把這稱做模板)不論使用哪一個定義,泛型的參數在真正使用泛型時都必須做出指明。函數
https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/generics/generic-classes 這是微軟對於泛型類的解釋。ui
其中指出了泛型類最經常使用於集合,如連接列表、哈希表、堆棧、隊列、樹等。 像從集合中添加和移除項這樣的操做都以大致上相同的方式執行,與所存儲數據的類型無關。this
知道定義了,那麼使用泛型有哪些好處呢? 這個最後再將,先看看java中的泛型。編碼
JAVA中的泛型:
先看下面這行代碼:
public class GenericTest { public static void main(String[] args) { List list = new ArrayList(); list.add("qqyumidi"); list.add(100); for (int i = 0; i < list.size(); i++) { String name = (String) list.get(i); // 1 System.out.println("name:" + name); } } }
能夠看出,這裏定義了一個List集合,先加入了String字符串,又加入了Integer類型的值,這是不報錯的,由於他們都是Object對象。編譯時是正常的,不出錯的,可是運行時會報出:「java.lang.ClassCastException」異常。所以,致使此類錯誤編碼過程當中不易發現。
上面的代碼告訴咱們兩個信息:
1:當咱們將一個對象放入集合中,集合不會記住此對象的類型,當再次從集合中取出此對象時,改對象的編譯類型變成了Object類型,但其運行時類型任然爲其自己類型。
2:所以,//1處取出集合元素時須要人爲的強制類型轉化到具體的目標類型,且很容易出現「java.lang.ClassCastException」異常。
泛型就是使集合可以記住集合內元素各種型。
下面有個例子:
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); } } }
採用泛型寫法後,在//1處想加入一個Integer類型的對象時會出現編譯錯誤,經過List<String>,直接限定了list集合中只能含有String類型的元素,從而在//2處無須進行強制類型轉換,由於此時,集合可以記住元素的類型信息,編譯器已經可以確認它是String類型了。
結合上面的泛型定義,咱們知道在List<String>中,String是類型實參,也就是說,相應的List接口中確定含有類型形參。且get()方法的返回結果也直接是此形參類型(也就是對應的傳入的類型實參)。下面就來看看List接口的的具體定義:
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的地方均表示相同的接受自外部的類型實參。
泛型接口、泛型類和泛型方法也是能夠自定義的:
上面的內容中,講解了泛型具體的運行過程,接口、類和方法也均可以用泛型去定義,以及相應的使用。 具體的使用過程當中,能夠分爲泛型接口、泛型類和泛型方法。
下面看一個自定義泛型接口,類,方法的例子,與List,ArrayList很類似:
package java_Collection; public class test01 { public static void main(String[] args){ Bos<String> bos = new Bos<String>("gogo"); System.out.println(bos.getData()); } } class Bos<T>{ private T data; public Bos(){ } public Bos(T data){ this.data = data; } public T getData(){ return data; } }
在泛型接口、泛型類和泛型方法的定義過程當中,咱們常見的如T、E、K、V等形式的參數經常使用於表示泛型形參,因爲接收來自外部使用時候傳入的類型實參。那麼對於不一樣傳入的類型實參,生成的相應對象實例的類型是否是同樣的呢?
public static void main(String[] args){ Bos<String> bos1 = new Bos<String>("gogo"); Bos<Integer> bos2 = new Bos<Integer>(1234); System.out.println("bos1 name:"+bos1.getClass()); //bos1 name:class java_Collection.Bos System.out.println("bos2 name:"+bos2.getClass()); //bos2 name:class java_Collection.Bos System.out.println(bos1.getClass()==bos2.getClass()); //true }
根據上面的實驗能夠得出,使用泛型的時候,傳進了不一樣的泛型實參,可是並無生成了真正的不一樣類型,在內存中傳入不一樣類型的泛型實參的泛型類只有一個,就是原先的最基本類型,咱們能夠從邏輯上理解成不一樣的類型。
具體緣由是什麼呢? 這與java提出泛型這一律唸的目的有關,致使其只是做用於代碼的編譯階段,在編譯過程當中,對於正確檢驗泛型結果後,會將泛型的相關信息擦出,也就是說,編譯成功的.class文件中是不包含任何泛型信息的。泛型的信息是不會進入到運行時階段。 能夠總結爲:泛型類型在邏輯上看以當作是多個不一樣的類型,實際上都是相同的基本類型。
下面就是類型通配符的概念了。
如今,Bos<Integer>和Bos<Number>實際上都是Bos類型,如今須要繼續探討:
那麼在邏輯上,相似於Box<Number>和Box<Integer>是否能夠當作具備父子關係的泛型類型呢?
下面看:
public class GenericTest { public static void main(String[] args) { Box<Number> name = new Box<Number>(99); Box<Integer> age = new Box<Integer>(712); getData(name); //The method getData(Box<Number>) in the type GenericTest is //not applicable for the arguments (Box<Integer>) getData(age); // 1 } public static void getData(Box<Number> data){ System.out.println("data :" + data.getData()); } }
在 //1處會報錯,The method getData(Box<Number>) in the t ype GenericTest is not applicable for the arguments (Box<Integer>)。顯然,Box<Number>在邏輯上不能視爲Box<Integer>的父類。那麼,緣由何在呢?
能夠用反證法說明。
好,那咱們回過頭來繼續看「類型通配符」中的第一個例子,咱們知道其具體的錯誤提示的深層次緣由了。那麼如何解決呢?總不能再定義一個新的函數吧。這和Java中的多態理念顯然是違背的,所以,咱們須要一個在邏輯上能夠用來表示同時是Box<Integer>和Box<Number>的父類的一個引用類型,由此,類型通配符應運而生。
類型通配符通常是使用 ? 代替具體的類型實參。注意了,此處是類型實參,而不是類型形參!且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); } public static void getData(Box<?> data) { System.out.println("data :" + data.getData()); } }
有時候,咱們還可能聽到類型通配符上限和類型通配符下限。具體有是怎麼樣的呢?
在上面的例子中,若是須要定義一個功能相似於getData()的方法,但對類型實參又有進一步的限制:只能是Number類及其子類。此時,須要用到類型通配符上限。
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()); } }
此時,顯然,在代碼//1處調用將出現錯誤提示,而//2 //3處調用正常。
類型通配符上限經過形如Box<? extends Number>形式定義,相對應的,類型通配符下限爲Box<? super Number>形式,其含義與類型通配符上限正好相反,在此不做過多闡述了。
注意: java中沒有所謂的泛型數組一說。