Java 高級特性泛型

概述

Generics(泛型),咱們常常在Java集合或者框架裏面常常看見對泛型的使用場景,那麼泛型的做用有哪些呢。java

  • 能夠幫助咱們處理多種數據類型執行相同的代碼
  • 數據安全性,泛型中的類型在使用時指定類型後,不須要強制類型轉換,最先起Java是沒有泛型的,它是使用Object來代替的,這樣程序員在寫程序的時候很容易出現類型轉換的錯誤。

泛型的本質是爲了參數化類型,就是將類型由原來的具體的類型參數化,就像方法中的變量參數,此時類型也定義成參數形式(能夠稱之爲類型形參),而後在使用/調用時傳入具體的類型(類型實參)。也就是說在泛型使用過程當中,操做的數據類型被指定爲一個參數,這種參數類型能夠用在類、接口和方法中,分別被稱爲泛型類、泛型接口、泛型方法。程序員

泛型類和泛型接口

泛型類/接口的定義,使用一個類型變量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;
    }
複製代碼

泛型中的約束和侷限性

  1. 不能用基本類型實例化類型參數
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);
    }
}
複製代碼
  1. 運行時類型查詢只適用於原始類型
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
複製代碼
  1. 泛型類的靜態上下文中類型變量失效

不能在靜態域或方法中引用類型變量。由於泛型是要在對象建立的時候才知道是什麼類型的,而對象建立的代碼執行前後順序是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的泛型實現方式有關,後續會說到

  1. 不能實例化類型變量
  2. 不能捕獲泛型類的實例
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

通配符類型

通常來講泛型的通配符有兩種,

  • ? extends X
    表示類型的上界,類型參數是X的子類或自身
  • ? super X
    表示類型的下界,類型參數是X的超類
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 泛型原理

Java語言中的泛型通常稱爲僞泛型,它只在程序源碼中存在,在編譯後的字節碼文件中,就已經替換爲原來的原生類型(Raw Type,也稱爲裸類型)了,而且在相應的地方插入了強制轉型代碼,所以,對於運行期的Java語言來講,ArrayList<int>與ArrayList<String>就是同一個類,因此泛型技術其實是Java語言的一顆語法糖,Java語言中的泛型實現方法稱爲類型擦除,基於這種方法實現的泛型稱爲僞泛型 因爲Java泛型的引入,各類場景(虛擬機解析、反射等)下的方法調用均可能對原有的基礎產生影響和新的需求,如在泛型類中如何獲取傳入的參數化類型等。所以,JCP組織對虛擬機規範作出了相應的修改,引入了諸如Signature、LocalVariableTypeTable等新的屬性用於解決伴隨泛型而來的參數類型的識別問題,Signature是其中最重要的一項屬性,它的做用就是存儲一個方法在字節碼層面的特徵簽名[3],這個屬性中保存的參數類型並非原生類型,而是包括了參數化類型的信息。修改後的虛擬機規範要求全部能識別49.0以上版本的Class文件的虛擬機都要能正確地識別Signature參數。 另外,從Signature屬性的出現咱們還能夠得出結論,擦除法所謂的擦除,僅僅是對方法的Code屬性中的字節碼進行擦除,實際上元數據中仍是保留了泛型信息,這也是咱們能經過反射手段取得參數化類型的根本依據

相關文章
相關標籤/搜索