java 泛型是java SE 1.5的新特性,泛型的本質是參數化類型,也就是說所操做的數據類型被指定爲一個參數。這種參數類型能夠用在類、接口和方法的建立中,分別稱爲泛型類、泛型接口、泛型方法。java
泛型(Generic type 或者 generics)是對 Java 語言的類型系統的一種擴展,以支持建立能夠按類型進行參數化的類。能夠把類型參數看做是使用參數化類型時指定的類型的一個佔位符,就像方法的形式參數是運行時傳遞的值的佔位符同樣。數組
泛型是在java SE 1.5中引入的,在這以前,若是要使用集合的話是怎麼樣子的呢?對於集合List而言,往裏面添加數據的方法add的參數是一個Object類型,這也就意味着什麼數據類型均可以往裏面添加。安全
List list = new ArrayList();
list.add("abc");
list.add("cdf");
複製代碼
然而,添加容易取出來就有點麻煩了。由於咱們取的時候須要進行強制轉換:bash
String s = (String) list.get(0);
複製代碼
有的時候,咱們忘了添加進去是什麼類型了,或者咱們寫的方法,類給別人使用的時候,這個時候就會很容易出錯,由於咱們不知道是什麼類型,所以在運行的時候就會出現異常。框架
List list = new ArrayList();
list.add("abc");
list.add("cdf");
list.add(12);
list.add(23.9);
for (int i = 0; i < list.size(); i++) {
String tempString = (String) list.get(i);
System.out.println(tempString);
}
複製代碼
對於上述引起的問題,咱們能夠經過限定傳入數據的類型來解決,就是在add數據的時候咱們就將它限定爲某一類型,其餘類型的數據add不進去,這樣的話,咱們就很簡單的解決了該問題。這種方法就是下面須要說的泛型。ide
List<String> list = new ArrayList<>();
list.add("abc");
list.add("cdf");
複製代碼
經過限定add時傳入的類型是String,這樣在編譯時就能檢測出來,這樣就能避免了上述問題。學習
泛型有三種使用方式,分別爲:泛型類、泛型接口、泛型方法。ui
泛型類提及來比較抽象,咱們經過一個示例來了解一下:this
/**
* 此處T能夠隨便寫爲任意標識,常見的如T、E、K、V等形式的參數經常使用於表示泛型
*
* @author admin
*
* @param <T>
*/
public class Generic<T> {
/**
* 這個成員變量的類型爲T,T的類型由外部指定
*/
private T t;
/**
* 泛型構造方法形參t的類型也爲T,T的類型由外部指定
*
* @param t
*/
public Generic(T t) {
super();
this.t = t;
}
/**
* 泛型方法getT的返回值類型爲T,T的類型由外部指定
*
* @return
*/
public T getT() {
return t;
}
/**
* 泛型方法setT的參數類型爲T,T的類型由外部指定
*
* @param t
*/
public void setT(T t) {
this.t = t;
}
}
複製代碼
泛型類比較簡單,須要指定一個參數類型T,而後由外部傳入,咱們看一下這個泛型類它的使用方式:spa
//泛型參數T這裏指定爲String類型
Generic<String> generic = new Generic<String>("Hello");
System.out.println(generic.getT());
複製代碼
打印結果以下:
Hello
複製代碼
這裏須要注意的是:泛型的類型參數只能是類類型(包括自定義類),不能是簡單類型。
泛型接口與泛型類的定義及使用基本相同。咱們也經過示例來了解一下:
/**
* 定義一個泛型接口
*
* @author admin
*
* @param <T>
*/
public interface IGeneric<T> {
T result();
}
複製代碼
定義一個泛型接口後,使用這個泛型接口有兩種狀況:
/**
* 未傳入泛型實參時,與泛型類的定義相同,在聲明類的時候,需將泛型的聲明也一塊兒加到類中
*
* @author admin
*
* @param <T>
*/
public class Generic1Test<T> implements IGeneric<T> {
@Override
public T result() {
return null;
}
}
複製代碼
/**
* 在實現類實現泛型接口時,如已將泛型類型傳入實參類型,則全部使用泛型的地方都要替換成傳入的實參類型
*
* @author admin
*
*/
public class Generic2Test implements IGeneric<String> {
@Override
public String result() {
return "";
}
}
複製代碼
泛型方法,是在調用方法的時候指明泛型的具體類型 ,可是相對於泛型類而言,泛型方法就比較複雜了,咱們來看一個具體示例:
/**
* 聲明一個泛型方法,該泛型方法中帶一個T類型形參,
*
* @param <T>
* @param a
* @param c
*/
static <T> void fromArrayToCollection(T[] a, List<T> c) {
for (T o : a) {
c.add(o);
}
}
複製代碼
申明一個泛型方法時,首先在public與返回值之間的必不可少,這代表這是一個泛型方法,而且聲明瞭一個泛型T,這個T能夠出如今這個泛型方法的任意位置,泛型的數量也能夠爲任意多個。
對於泛型參數的申明比較複雜的多,後面須要專門的講解。
咱們在定義泛型類,泛型方法,泛型接口的時候常常會遇見不少不一樣的通配符,好比 T,E,K,V ,?等等,這些通配符又都是什麼意思呢?
其實,這些不一樣的通配符沒什麼本質上的區別,只不過在Java開發過程當中咱們約定了這些不一樣的通配符所表達的意思不同:
對於通配符咱們能夠理解爲無界,就是, 好比List,通配符的主要做用就是讓泛型可以接受未知類型的數據。若是泛型的類型只在方法聲明中出現一次,就能夠用通配符<?>取代它。
咱們說明一點:List<?> list和List list的區別:
注意:List<?> list能夠add(null),由於null是任何引用數據類型都具備的元素。
用 extends 關鍵字聲明,表示參數化的類型多是所指定的類型,或者是此類型的子類。
用 super 進行聲明,表示參數化的類型多是所指定的類型,或者是此類型的父類型,直至 Object。
Java的泛型是僞泛型,這是由於Java在編譯期間,全部的泛型信息都會被擦掉,正確理解泛型概念的首要前提是理解類型擦除。Java的泛型基本上都是在編譯器這個層次上實現的,在生成的字節碼中是不包含泛型中的類型信息的,使用泛型的時候加上類型參數,在編譯器編譯的時候會去掉,這個過程成爲類型擦除。
譬如: 對於List類型,在編譯後會變成List,JVM看到的只是List,而由泛型附加的類型信息對JVM是看不到的。Java編譯器會在編譯時儘量的發現可能出錯的地方。
咱們舉個例子來講明泛型擦除:
public static void main(String[] args) {
List<String> strList = new ArrayList<String>();
strList.add("AAAAA");
List<Integer> intList = new ArrayList<>();
intList.add(123);
System.out.println("編譯時期類型是否相同: " + (strList.getClass() == intList.getClass()));
}
複製代碼
打印結果以下:
從上面的例子看出,咱們定義了兩個ArrayList數組,不過一個是ArrayList泛型類型的,只能存儲字符串;一個是ArrayList泛型類型的,只能存儲整數,最後,咱們經過list1對象和list2對象的getClass()方法獲取他們的類的信息,最後發現結果爲true。說明泛型類型String和Integer都被擦除掉了,只剩下原始類型。
Java 語言中引入泛型是一個較大的功能加強。不只語言、類型系統和編譯器有了較大的變化,以支持泛型,並且類庫也進行了大翻修,因此許多重要的類,好比集合框架,都已經成爲泛型化的了。
在上面所舉的例子都是一些簡單的示例並不具備實際的應用,只是爲了簡單說明泛型的概念。本篇文章只是做爲泛型的簡單闡述,對於開發過程當中的泛型還須要咱們深刻的探索,後期會出一遍深刻理解泛型的文章,敬請期待。
專一於 Android 開發多年,喜歡寫 blog 記錄總結學習經驗,blog 同步更新於本人的公衆號,歡迎你們關注,一塊兒交流學習~