泛型是一種參數化類型的機制。它可使得代碼適用於各類類型,從而編寫更加通用的代碼,例如集合框架。java
泛型是一種編譯時類型確認機制。它提供了編譯期的類型安全,程序員
1,類型安全。 泛型的主要目標是提升 Java 程序的類型安全。經過知道使用泛型定義的變量的類型限制,編譯器能夠在一個高得多的程度上驗證類型假設。沒有泛型,這些假設就只存在於程序員的頭腦中(或者若是幸運的話,還存在於代碼註釋中)。數組
2,消除強制類型轉換。 泛型的一個附帶好處是,消除源代碼中的許多強制類型轉換。這使得代碼更加可讀,而且減小了出錯機會。安全
3,潛在的性能收益。 泛型爲較大的優化帶來可能。在泛型的初始實現中,編譯器將強制類型轉換(沒有泛型的話,程序員會指定這些強制類型轉換)插入生成的字節碼中。可是更多類型信息可用於編譯器這一事實,爲將來版本的 JVM 的優化帶來可能。因爲泛型的實現方式,支持泛型(幾乎)不須要 JVM 或類文件更改。全部工做都在編譯器中完成,編譯器生成相似於沒有泛型(和強制類型轉換)時所寫的代碼,只是更能確保類型安全而已。框架
定義泛型類 在 類名後面 加上<T> 表示這是個泛型類ide
public class Crate<T> { private T contents; public T emptyCrate() { return contents; } public void packCrate(T contents) { this.contents = contents; } }
這個泛型類型,能夠在類內任何地方出現,函數
如 屬性類型,方法的返回值,方法的參數類型。性能
在生成實例的時候 必須指定具體類型。優化
// create an instance with generic type Crate<Elephant> crateForElephant = new Crate<>();
泛型數量能夠是多個this
public class SizeLimitedCrate<T, U> { private T contents; private U sizeLimit; public SizeLimitedCrate(T contents, U sizeLimit) { this.contents = contents; this.sizeLimit = sizeLimit; } } // create an instance with generic types SizeLimitedCrate<Elephant, Integer> c1 = new SizeLimitedCrate<>()
泛型類命名規範
理論上來講,泛型的類型名字能夠定義成任何你想要的。爲了方便起見,提升可讀性,JDK建議你們採用 單個大寫字母,區分泛型與真實類名,同時提供了一些經常使用的建議泛型
E 表示一個元素
K 表示一個鍵值對的鍵
V 表示一個鍵值對的值
N 表示一個數字
T 表示一個通用類型
若是是多個通用類型,能夠延續使用,S, U, V, .
泛型類支持接口定義, 即定義一個泛型接口。
public interface Shippable<T> { void ship(T t); }
那麼問題來了,這個泛型類怎麼去實現?有三種方式能夠實現
在實現接口的同時 指定具體的類型 而不用泛型表示。
class ShippableRobotCrate implements Shippable<Robot> { public void ship(Robot t) { } }
在實現接口的同時,本身也變成泛化類,進一步,泛化下沉。
class ShippableAbstractCrate<U> implements Shippable<U> { public void ship(U t) { } }
在實現接口的同時,不繼續使用泛型,取而代之的是Object類型,這是個古老方法,主要是爲了向前兼容,對那些沒有泛型支持的兼容。
對於編譯器而言,會拋出警告,可是會經過編譯。
class ShippableCrate implements Shippable { public void ship(Object t) { } }
1. 構造函數不能泛型, 如new T() 最終變成 new Object()
2. 不能使用靜態類型的數組
3. 不能使用instanceof, 運行的時候泛型會被擦除
4. 不能使用基本類型做爲泛型的參數,能夠經過封裝類如:Integer
5. 不能使用靜態類型做爲參數
泛型方法 與 泛型類有點相似,只是它做用與具體的方法,範圍相對於泛型類 更小。
在定義方法的時候 在聲明返回值的前面 使用<T> 來聲明泛型方法。
public static <T> void sink(T t) { }
對於方法的返回類型,也能夠是泛型或者是泛型類。
// 返回一個泛型 public static <T> T identity(T t) { return t; } // 返回一個泛型類 public static <T> Crate<T> ship(T t) { System.out.println("Preparing " + t); return new Crate<T>(); }
一樣的, 泛型方法支持多個 泛型類型
// 返回一個泛型 public static <T,U> T identity(T t,U u) { return t; }
調用具體的泛型方法時,須要指定具體類型
Box.<String>ship("package"); Box.<String[]>ship(args);
調用泛型方法的時候能夠向正常的方法調用同樣, java編譯器會自動匹配泛型
Box.ship("package");
泛型的出現幫助編譯器可以在編譯的時候,使用正確的類型。
實際上,編譯器 是將全部的泛型替換爲 Object,換句話說,代碼編譯以後,這些泛型都將被Object所取代。這麼作的目的主要是爲了兼容老版本的代碼(非泛型)
public class Crate { private Object contents; public Object emptyCrate() { return contents; } public void packCrate(Object contents) { this.contents = contents; } }
也不用過於擔憂 這個泛型擦除,編譯期會自動轉型了那些被擦除了泛型 如:
當你調用方法: Robot r = crate.emptyCrate();
編譯期 實際會編譯出顯示轉型的代碼
Robot r = (Robot) crate.emptyCrate();
class Dragon {} class Unicorn { } public class LegacyDragons { public static void main(String[] args) { List unicorns = new ArrayList(); unicorns.add(new Unicorn()); printDragons(unicorns); } private static void printDragons(List<Dragon> dragons) { for (Dragon dragon: dragons) { // ClassCastException System.out.println(dragon); } } }
雖然有了泛型擦除,但java 畢竟是動態強類型語言,在實際使用過程當中,與老代碼結合的使用也會出現問題。
泛型的通配表示的是爲知類型,經過? 表示
對一個泛型的通配有三種方式來使用它
類型 | 語法 | Example |
無界通配 | ? | List<?> l =new ArrayList<String>(); |
上界通配 |
? extends type | List<? extends Exception> l =new ArrayList<RuntimeException> (); |
下界通配 |
? super type | List<? super Exception> l =new ArrayList<Object>(); |
java 是強類型語言, 因此,對於
List<Object> keywords = new ArrayList<String>();
是不能經過編譯的, 若是使用了通配就可。
public static void printList(List<?> list) { for (Object x: list) System.out.println(x); } public static void main(String[] args) { List<String> keywords = new ArrayList<>(); keywords.add("java"); printList(keywords); }
假如 咱們要設定一個繼承關係的泛型
ArrayList<Number> list = new ArrayList<Integer>(); // DOES NOT COMPILE
List<? extends Number> list = new ArrayList<Integer>(); // compiled
上界通配表示 任何一個 Number的子類包括它本身均可以被匹配進來
public static long total(List<? extends Number> list) { long count = 0; for (Number number: list) count += number.longValue(); return count; } // 有了上界的泛型,在基於泛型擦除的機制,會將Object 強轉成泛型上界 public static long total(List list) { long count = 0; for (Object obj: list) { Number number = (Number) obj; count += number.longValue(); } return count; }
須要注意的是,使用了上界通配的列表 是不能添加元素,從java的角度來看,編譯期並不知道
添加的元素的具體是哪個,由於任何extends type均可能。
static class Sparrow extends Bird { } static class Bird { } public static void main(String[] args) { List<? extends Bird> birds = new ArrayList<Bird>(); birds.add(new Sparrow()); // DOES NOT COMPILE birds.add(new Bird()); // DOES NOT COMPILE }
與上界通配相似,表示 任何一個 超類包括它本身均可以被匹配進來
public static void addSound(List<? super String> list) { // lower bound list.add("quack"); }
在使用泛型的時候能夠遵循一些基本的原則,從而避免一些常見的問題。