Generics(泛型),咱們常常在Java集合或者框架裏面常常看見對泛型的使用場景,那麼泛型的做用有哪些呢。java
泛型的本質是爲了參數化類型,就是將類型由原來的具體的類型參數化,就像方法中的變量參數,此時類型也定義成參數形式(能夠稱之爲類型形參),而後在使用/調用時傳入具體的類型(類型實參)。也就是說在泛型使用過程當中,操做的數據類型被指定爲一個參數,這種參數類型能夠用在類、接口和方法中,分別被稱爲泛型類、泛型接口、泛型方法。程序員
泛型類/接口的定義,使用一個類型變量T(其餘大寫字母均可以,不過經常使用的就是T,E,K,V等等),至關於一個佔位符,而且用<>括起來,並放在類/接口名的後面。泛型類是容許有多個類型變量的數組
public class GenericsClass<T> {
private void print(){
System.out.println("泛型類型");
}
public static void main(String [] args){
GenericsClass<String> genericsClass = new GenericsClass<>();
genericsClass.print();
}
}
複製代碼
public class GenericsClass<T,V> {
private void print(){
System.out.println("泛型類型");
}
public static void main(String [] args){
GenericsClass<String,Integer> genericsClass = new GenericsClass<>();
genericsClass.print();
}
}
複製代碼
public interface ImpGenerics<V> {
}
複製代碼
而實現泛型接口的類,有兩種實現方法安全
public class Generics<T> implements ImpGenerics<T> {
}
複製代碼
這中實現的時候,沒有指定具體的類型,這種實現方式在建立對象的時候,須要傳入具體的類型bash
public class Generics implements ImpGenerics<String> {
}
複製代碼
這種是在實現的時候傳入具體的實參,建立對象的時候就想普通類同樣使用就OK框架
泛型方法,是在調用方法的時候指明泛型的具體類型 ,泛型方法能夠在任何地方和任何場景中使用,包括普通類和泛型類,ide
public class GenericsClass<K,V> {
private K date;
private V value;
//普通方法
private V getValue(K date){
return value;
}
//泛型方法
private <T> T genericsMethod(T date){
return date;
}
public GenericsClass(K date, V value) {
this.date = date;
this.value = value;
}
private void print(){
System.out.println("泛型類型");
}
public static void main(String [] args){
GenericsClass<String,Integer> genericsClass = new GenericsClass<>("k",123);
genericsClass.print();
int a = genericsClass.getValue("k");
System.out.println("v="+a);
}
}
複製代碼
泛型方法必須經過「<類型佔位符>」來聲明返回的類型,譬如<V>等函數
一般在使用時候,咱們須要讓全部的類型具體同一個方法,咱們須要對類型變量加以約束,好比計算兩個變量的最小,最大值,爲了確保傳入的兩個變量必定有compareTo方法?咱們就須要將T限制爲實現了接口Comparable的類ui
private <T extends Comparable> T min(T a,T b){
return a.compareTo(b)>0 ? b:a;
}
複製代碼
T extends Comparable中 T表示應該綁定類型的子類型,Comparable表示綁定類型,子類型和綁定類型能夠是類也能夠是接口,同時extends左右都容許有多個,如 T,V extends Comparable & Serializable 注意限定類型中,只容許有一個類,並且若是有類,這個類必須是限定列表的第一個。 這種類的限定既能夠用在泛型方法上也能夠用在泛型類上this
private <T extends Comparable & Serializable> T min(T a, T b){
return a.compareTo(b)>0 ? b:a;
}
複製代碼
public class GenericsClass<K,V> {
private K date;
private V value;
//普通方法
private V getValue(K date){
return value;
}
//泛型方法
private <T> T genericsMethod(T date){
return date;
}
private <T extends Comparable & Serializable> T min(T a, T b){
return a.compareTo(b)>0 ? b:a;
}
public GenericsClass(K date, V value) {
this.date = date;
this.value = value;
}
private void print(){
System.out.println("泛型類型");
}
public static void main(String [] args){
//不能使用基本類型
GenericsClass<String,int> genericsClass = new GenericsClass<>("k",123);
}
}
複製代碼
public class GenericsClass<K> {
private K date;
private void print(){
System.out.println("泛型類型");
}
public static void main(String [] args){
GenericsClass<String> genericsClass = new GenericsClass<>();
GenericsClass<String> genericsClass_ = new GenericsClass<>();
System.out.println("泛型類型genericsClass="+genericsClass_.getClass().getName().toString());
System.out.println("泛型類型genericsClass_="+genericsClass_.getClass().getName().toString());
}
}
複製代碼
輸出
泛型類型genericsClass=com.mtx.javalib.GenericsClass
泛型類型genericsClass_=com.mtx.javalib.GenericsClass
複製代碼
不能在靜態域或方法中引用類型變量。由於泛型是要在對象建立的時候才知道是什麼類型的,而對象建立的代碼執行前後順序是static的部分,而後纔是構造函數等等。因此在對象初始化以前static的部分已經執行了,若是你在靜態部分引用的泛型,那麼毫無疑問虛擬機根本不知道是什麼東西,由於這個時候類尚未初始化。 4. 不能建立參數化類型的數組
public class GenericsClass<K> {
private K date;
private void print(){
System.out.println("泛型類型");
}
public static void main(String [] args){
//編譯會報錯
GenericsClass<String>[] genericsClass = new GenericsClass<String>()[3];
}
}
複製代碼
爲何是這樣,主要是Java的泛型實現方式有關,後續會說到
public <T extends Throwable> T testMethod(T t){
try {
System.out.println("泛型類型異常沒法捕獲");
}catch (T e){
}
return t;
}
複製代碼
可是這種方式是能夠的
public <T extends Throwable> T testMethod(T t){
try {
System.out.println("泛型類型異常沒法捕獲");
}catch (Throwable e){
}
return t;
}
複製代碼
泛型類能夠繼承或者擴展其餘泛型類,好比List和ArrayList
通常來講泛型的通配符有兩種,
public class Car {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Bmw extends Car {
@Override
public String getName() {
return super.getName();
}
@Override
public void setName(String name) {
super.setName(name);
}
}
複製代碼
public class Bmw extends Car {
@Override
public String getName() {
return super.getName();
}
@Override
public void setName(String name) {
super.setName(name);
}
public static void testMethod(GenericsClass<? extends Car> car){
System.out.println("類型參數只能是Car的子類");
}
public static void main(String[] args){
GenericsClass<Bmw> genericsClass = new GenericsClass();
testMethod(genericsClass);
}
}
複製代碼
Java語言中的泛型通常稱爲僞泛型,它只在程序源碼中存在,在編譯後的字節碼文件中,就已經替換爲原來的原生類型(Raw Type,也稱爲裸類型)了,而且在相應的地方插入了強制轉型代碼,所以,對於運行期的Java語言來講,ArrayList<int>與ArrayList<String>就是同一個類,因此泛型技術其實是Java語言的一顆語法糖,Java語言中的泛型實現方法稱爲類型擦除,基於這種方法實現的泛型稱爲僞泛型 因爲Java泛型的引入,各類場景(虛擬機解析、反射等)下的方法調用均可能對原有的基礎產生影響和新的需求,如在泛型類中如何獲取傳入的參數化類型等。所以,JCP組織對虛擬機規範作出了相應的修改,引入了諸如Signature、LocalVariableTypeTable等新的屬性用於解決伴隨泛型而來的參數類型的識別問題,Signature是其中最重要的一項屬性,它的做用就是存儲一個方法在字節碼層面的特徵簽名[3],這個屬性中保存的參數類型並非原生類型,而是包括了參數化類型的信息。修改後的虛擬機規範要求全部能識別49.0以上版本的Class文件的虛擬機都要能正確地識別Signature參數。 另外,從Signature屬性的出現咱們還能夠得出結論,擦除法所謂的擦除,僅僅是對方法的Code屬性中的字節碼進行擦除,實際上元數據中仍是保留了泛型信息,這也是咱們能經過反射手段取得參數化類型的根本依據