首頁 > 軟體

一起來學習Java的泛型

2022-03-16 10:01:19

泛型:

什麼是泛型?

泛型是在Java SE 1.5引入的的新特性,本質是引數化型別,也就是說所操作的資料型別被指定為一個引數。這種引數型別可以用在類、介面和方法的建立中,分別稱為泛型類、泛型介面、泛型方法。

簡而言之:<>泛型就是用來約束類、方法、屬性上的資料型別,比如

List<Integer> list = new ArrayList<Integer>();List<Integer> list = new ArrayList<Integer>();

new ArrayList這個集合的元素只能新增Integer型別。

為什麼需要泛型?

Java推出泛型之前,程式設計師可以構建一個Object型別的集合,該集合能夠儲存任何的資料型別,而在使用該 集合的時候,需要程式設計師明確知道每個元素的具體的型別並向下轉型,否則容易引發ClassCastException 類轉換異常。現在我們通過泛型就能解決這個問題,通過泛型<>我們就能夠約束這個集合插入的型別,就不需要再從Object型別轉換成子類型別。

泛型有什麼好處?

  • 型別安全,不會插入指定型別以外的資料
  • 消除了強制型別轉換

泛型的型別:

泛型可以定義在類上、父類別上、介面上、子類上、方法上、引數上、屬性上, 泛型型別是可以用任意字母來代替你需要傳遞的資料,一般為了可讀性,我們約定一般會寫:

  • E -element 程式碼集合中存放的元素
  • T -Type表示型別Java類
  • K -key 表示鍵
  • V -V 表示value
  • N -Number 表示數值型別
  • ? 表示不確定的型別

那麼這麼多字母有什麼用呢?我們來看一段程式碼:這是一個普通的Student類:

package Test;
public class Student {
    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

我們升級一下,把這個類定為String的泛型,意思是這個類只能接收String的型別:

public class Student<Stirng> {   //重複的程式碼就不重寫了,和上面的是一樣
}

ok,這樣沒問題,但是我們想一想,如果直接就把這個類的泛型定義死了,如果後面需要在Student傳入Integer型別,我們就得重寫一個Student類,這樣程式碼的複用性不高,不符合程式設計思維,所以我們可以這樣:

public class Student<T> {
        private T t;
        public T getT() {return t;}
        public void setT(T t) {this.t = t;}
    }

然後當需要用什麼泛型的時候直接傳入即可:

Student<String> stu1 = new Student<>();
Student<Integer> stu2 = new Student<>();
Student<Double> stu3 = new Student<>();

測試:(接上面的程式碼)

public class Test {
    public static void main(String[] args) {
        //先使用Integer泛型,傳入100
        Student<Integer> stu1 = new Student<>(100);
        Integer in = stu1.getT();
        System.out.println(in);
    }
}

輸出:100 (沒問題)

但是如果在定義為Integer下輸入String型別,會怎麼樣?

很明顯,直接就報錯了

這樣就更好的利用了程式碼的複用性。上面部分的知識點也就是泛型類。

泛型父類別和子類:

我們先定義一個泛型父類別:()

public class Student<T> {
    private T t;
    public T getT() {return t;}
    public void setT(T t) {this.t = t;}
}

然後定義一個子類,繼承該Student父類別:

public class Child<T> extends Student<T>{
    @Override
    public T getT(){
        return super.getT();
    }
    @Override
    public void setT(T t){
        super.setT(t);
    }
}

特別需要注意:

1.泛型子類的引數一定要和父類別的引數型別一致

2.如果子類沒有新增泛型,那麼父類別的引數型別必須明確

也就是這樣:

public class Child extends Student<Integer>{
}

測試:下面程式碼的解釋:在父類別Student內定義泛型為String,然後建立子類Child的物件child,因為是繼承關係,所有child的泛型也是String,

public class Test {
    public static void main(String[] args) {
        Student<String> child = new Child<>();  //多型
        child.setT("abc");
        String value = child.getT();
        System.out.println(value);
    }
}

當子類傳入非String型別值時:

泛型介面:

泛型介面其實也很簡單,定義如下:

public interface USB<T> {   
}

泛型子類實現泛型介面,除了處理標識父類別的泛型標識外,還可以繼續擴充套件泛型:

public class phone<T,V> implements USB<T>{  
//這裡的意思是子類除了有USB介面的泛型T類,也可以再擴充套件一個泛型
    private T color; //手機顏色
    private V phoneName;  //手機名稱
    public phone(T color,V phoneName) {
        this.color = color;
        this.phoneName = phoneName;
    }
    public T getColor() {
        return color;
    }
    public void setColor(T color) {
        this.color = color;
    }
    public V getPhoneName() {
        return phoneName;
    }
    public void setPhoneName(V phoneName) {
        this.phoneName = phoneName;
    }
}

注意: 泛型介面也和泛型父子類一樣,如果子類沒有新增泛型引數,那麼父類別一定要明確的指定型別!

public class phone implements USB<String>{
}

測試

分別把T和V的泛型都定義為String:

public class Test {
    public static void main(String[] args) {
        phone<String,String> ph = new phone<>("黑色","華為");
        String str1 = ph.getColor();
        String str2 = ph.getPhoneName();
        System.out.println(str1+str2);
    }
}

泛型方法:

定義泛型方法:在public和返回值之間定義<>的才是泛型方法:

    public<E> void printType(){
    }

然而這個方法的返回值型別就是取決於E的型別,如果E是Integer,那麼這個方法的返回值型別就是整數。用法基本和上述的一致,需要什麼型別的返回值,在呼叫的時候去傳遞泛型型別即可。

萬用字元:

萬用字元一般是使用 ? 代替具體的型別實參(此處是型別實參,而不是型別形參)。當操作型別時不需要使用型別的具體功能時,只使用Object類中的功能,那麼可以用 ? 萬用字元來表未知型別。例如 List<?> 在邏輯上是List、List 、List等所有List<具體型別實參>的父類別。

有界的型別引數:

有的時候需要限制那些被允許傳遞到一個型別引數的型別種類範圍,例如一個運算元字的方法可能只希望接受Number或者Number子類的範例。這時就需要為泛型新增上邊界,即傳入的型別實參必須是指定型別的子型別。要宣告一個有界的型別引數,首先列出型別引數的名稱,後跟extends或super關鍵字,最後緊跟它的上界或下界。由此可以知道泛型的上下邊界的新增必須與泛型的宣告在一起 。

上限: <? extends T>

表示該萬用字元所代表的型別是T型別的子類。 例如往集合中新增元素時,既可以新增T型別物件,又可以新增T的子型別物件。

下限:<? super T>

表示該萬用字元所代表的型別是T型別的父類別,就是隻能獲取到T類及以上的泛型,任何繼承T類的泛型將得不到。

舉例說明:

建一個動物的父類別:

public class Animal {
}

建一個貓類,繼承父類別:

public class Cat extends Animal{
}

建一個小貓類,繼承貓類:

public class MiniCat extends Cat{
}

現在的關係是Animal>Cat>MiniCat

萬用字元上限的測試類:

public class test {
    public static void main(String[] args) {
        ArrayList<Animal> animals = new ArrayList<>();
        ArrayList<Cat> cats = new ArrayList<>();
        ArrayList<MiniCat> minicats = new ArrayList<>();
        showAnimals(cats);
        showAnimals(minicats);
        //這樣會報錯,因為在showAnimals方法內的萬用字元最高繼承自Cat
        showAnimals(animals);
    }
    //使用萬用字元,把上線限制到Cat,意思是Animal這個類不能被獲取
    private static void showAnimals(ArrayList<? extends Cat> cats) {
        for (Cat cat:cats
             ) {
            System.out.println(cat);
        }
    }
}

可以看到使用萬用字元限制到Cat類後,Animals這個類就已經獲取不到了。這就是上限。

如果把上限設定成Animal的時候:

萬用字元下限的測試類:(其他程式碼與上面的一致)

public class test {
    public static void main(String[] args) {
        ArrayList<Animal> animals = new ArrayList<>();
        ArrayList<Cat> cats = new ArrayList<>();
        ArrayList<MiniCat> minicats = new ArrayList<>();
        showAnimals(cats);
        showAnimals(minicats);
        //這樣會報錯,因為在showAnimals方法內的萬用字元最高繼承自Cat
        showAnimals(animals);
    }
    //使用萬用字元,把上線限制到Cat,意思是Animal這個類不能被獲取
    private static void showAnimals(ArrayList<? extends Cat> cats) {
        for (Cat cat:cats
             ) {
            System.out.println(cat);
        }
    }
}

因為萬用字元設定了下限到Cat類,所以MiniCat是獲取不到的:

總結

以上就是關於泛型和萬用字元的介紹,泛型可以在類、介面、方法中使用,分別簡稱之泛型類、泛型介面、泛型方法,並且通過泛型實現資料型別的任意化,即靈活、又有安全性,易於維護。在編譯過中,對於正確檢驗泛型結果後,會將泛型的相關資訊擦出,也就是說,成功編譯過後的class檔案中是不包含任何泛型資訊的。泛型資訊不會進入到執行時階段。

總而言之,泛型是在編譯的時候檢查型別安全,所有的強制轉換都是自動和隱式的,提高程式碼的重用率。並且消除強制型別轉換,在傳入引數的時候就已經限制的型別。

本篇文章就到這裡了,希望能夠給你帶來幫助,也希望您能夠多多關注it145.com的更多內容!     


IT145.com E-mail:sddin#qq.com