在Java中通常的類和方法,只能使用具體的類型,要麼是基本數據類型,要麼是自定義類型。若是要編寫能夠應用於多種類型的代碼,這種刻板的限制就會束縛不少!java
1.多態:將方法的參數類型設爲基類,那麼該方法就能夠接收從這個基類導出的任何類做爲參數。編程
class Primary{} //定義基類 class Test() { public void f(Primary p) {...} }
2.方法的參數使用接口:任何實現了該接口的類均可以知足該方法。app
interface Primary{} //定義接口 class Test() { public void f(Primary p) //實現了該接口的全部類均可以做爲參數 {...} }
3.使用泛型。dom
泛型實現了參數化類型的概念,使代碼能夠應用於某種不具體的類,而不是具體的一個類或者接口。
簡單說就是使代碼能夠適用於普遍的類型。spa
1.當程序調用泛型方法時,若是擦除了泛型返回類型,編譯器插入類型轉換。翻譯
Pair<Employee> buddies = ... Employee buddy = buddies.getFirst();
• 也就是說,編譯其把這個方法調用翻譯爲兩條虛擬指令:
◇ 對原始方法Pair.getFirest的調用。
◇ 將返回的Object類型,強制轉換爲Employee類型。
2.當存取一個泛型域時也要插入強制類型轉換。
假設 Pair 類的first 域 和 second 域都是 公有的(這不是種好的編程風格, 但在java語法中,這是合法的)。
表達式: Employee buddy = buddies.first; 也會在結果字節碼中插入強制類型轉換; 3d
使用泛型預約義參數類型:rest
說明:基本數據類型沒法做爲類型參數code
在使用時具體化類型參數對象
小結
泛型類最主要的使用是應用在集合中,代碼封裝了一個ArrayList,這樣咱們在編寫類的時候,就不會受限於具體的類,由於具體使用的類型是在初始化對象的時候才指定的。
咱們爲何要這樣呢?若是這個實現類不用泛型,若是處理多種類型數據的時候,就要編寫多個實現類,來針對處理。
泛型也能夠應用於接口,類和接口的類型參數應該保持一致,都是T或者其餘。
泛型接口最經常使用的一個用法是實現Iterable接口,實現迭代方法!
import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.Random; public class RandomList<T> implements Iterable<T> { private ArrayList<T> storage = new ArrayList<T>(); private Random rand = new Random(new Date().getTime()); public void add(T item) { storage.add(item); } public T select() { return storage.get(rand.nextInt(storage.size())); } /* 實現Iterable接口 */ public Iterator<T> iterator() { return storage.iterator(); } }
是否擁有泛型方法和其是不是泛型類並沒有直接關係,也就是說泛型方法能夠獨立於類而產生變化!
在泛型方法中,能夠顯式地指明參數類型,不過不多這樣使用!
泛型方法和可變參數列表能夠很好的共存
1.添加限定來讓類型變量具備特定功能。
2.能夠有多個限定(接口和類都是能夠的),但要注意順序。
<T extends BoundingType>
這句代碼表示T應該是綁定類型(BoundingType)的子類型。T和綁定類型能夠是類,也能夠是接口。
若T是接口,那麼方法調用時傳進來的是T的實現類也是能夠的。extends更接近於子類的概念,因此選用extends。
咱們極可能會認爲c1!=c2,結果爲false,由於c1不能裝入Integer,c2也不能裝入String。
在泛型代碼內部,沒法獲取任何有關泛型參數類型的信息,也就是你沒法得到那個具體的類型是什麼,你僅僅能夠獲知的諸如類型參數標識符和泛型邊界這類的信息。
這意味者當你在使用泛型的時候,任何具體的類型信息都被擦除了,你惟一知道的就是你在使用一個對象。所以問題中c1==c2,爲true。這兩種形式都被擦除成爲它們的「原生」類型,即List.
虛擬機沒有泛型類型對象,全部對象都屬於普通類。不管什麼時候定義了一個泛型類型,都自動提供了一個相應的原始類型,將泛型類型還原成原始類型的過程,稱爲擦除。
原始類型的名字就是刪去類型參數後的泛型類型名。擦除類型變量,並替換爲限定類型(無限定類型的變量默認用Object)。
左圖的原始類型變爲:public class Test<Object>,全部T都變成Object 右圖變爲:public class Test<Comparable>,全部T都變成Object。
說明:
這樣就不難理解,爲何,加上限定邊界後,咱們就能夠調用obj.compareTo()了,由於擦除類型變量後,T都被還原成Comparable,效果就是任何實現了Comparable的子類均可以調用Test對象的compare()方法。若是泛型是在Java 1.0就出現的,那麼這個特性將不會使用擦除來實現——它將使用具體化,使類型參數保持爲第一類的實體,所以你就可以在類型參數上執行基於類型的語言操做和反射操做。
Test<Cat> test = new Test<Cat>();
看起來class Test應該知道如今工做於Cat之上,而泛型語法也在強烈暗示,在整個類中的各個地方,類型T都在被替換。可是事實並不是如此,不管什麼時候,當你在編寫這個類的代碼時,必須時刻提醒本身:它僅僅只是一個Object。
擦除的補償。
1.邊界使得你能夠在用於泛型的類型參數上設置限制條件。儘管這使得你能夠強制規定泛型能夠應用的類型,可是其潛在的一個更重要的效果是你能夠按照本身的邊界類型來調用方法。
2.由於擦除移除了類型信息,因此,能夠用無界泛型參數調用的方法只能是那些能夠用Object調用的方法,那麼你就能夠用這些類型字節來調用方法。
1.Java泛型是強制類型檢測的,泛型類型的子類型互不相關。
publicclass Test { public static void main(String[] args) throws Exception{ List<Integer> listInteger =new ArrayList<Integer>(); List<String> listString =new ArrayList<String>(); printCollection(listInteger); printCollection(listString); } public static void printCollection(Collection<Object> collection){ for(Object obj:collection){ System.out.println(obj); } } } //Integer String都是Object的子類型,可是結果會報錯,這就說明了泛型不考慮繼承關係 The method printCollection(Collection<Object>) in the type GernericTest is not applicable for the arguments (List<Integer>)
2.咱們但願泛型能向普通類那樣具備面向對象的一些特徵:
3.爲了使泛型能具備面向對象的一些繼承關係,Java引入了通配符的一些概念:無界通配符 ?
說明:
? extends T 的本質上的實現是泛型的自動向上轉型。
使用方法和解釋同上。