Java泛型

基本概念

  • 通常情況下集合中可以存放不同類型的對象,是因?yàn)閷⑺袑ο蠖伎醋鯫bject類型放入的,因此從集合中取出元素時也是Object類型,為了表達(dá)該元素真實(shí)的數(shù)據(jù)類型,則需要強(qiáng)制類型轉(zhuǎn)換,而強(qiáng)制類型轉(zhuǎn)換可能會引發(fā)類型轉(zhuǎn)換異常。

例子:

public class ListTest {
    public static void main(String[] args) {
        List list = new LinkedList();
        list.add(0, 1);
        list.add(1, true);
        list.add(2, "String");
        String s = (String)list.get(2);
        System.out.println(s);
    }
}
  • 為了避免上述錯誤的發(fā)生,從Java5開始增加泛型機(jī)制,也就是在集合名稱的右側(cè)使用<數(shù)據(jù)類型>的方式來明確要求該集合中可以存放的元素類型,若放入其它類型的元素則編譯報(bào)錯。
  • 泛型只在編譯時期有效,在運(yùn)行時期不區(qū)分是什么類型。
public class ListGenericityTest {
    public static void main(String[] args) {
        // 1.準(zhǔn)備一個支持泛型機(jī)制的List集合,明確要求集合中的元素是String類型
        List<String> lt1 = new LinkedList<>();
        // 2.向集合中添加元素并打印
        lt1.add("one");
        System.out.println("lt1 = " + lt1); // one
        // lt1.add(2); Error
        // 3.獲取集合中的元素并打印
        String s = lt1.get(0);
        System.out.println("獲取到的元素是:" + s); // one
        System.out.println("============================================================");
        // 4.準(zhǔn)備一個支持Integer類型的List集合
        List<Integer> lt2 = new LinkedList<>();
        lt2.add(1);
        lt2.add(2);
        // lt2.add("3"); Error
        System.out.println("lt2 = " + lt2); // [1, 2]
        Integer integer = lt2.get(0);
        System.out.println("獲取到的元素是:" + integer); // 1
        System.out.println("============================================================");
        // Java7開始的新特性:菱形特性,也就是后面<>中的數(shù)據(jù)類型可以省略
        List<Double> lt3 = new LinkedList<>();
        // 筆試考點(diǎn)
        // 視圖將lt1的數(shù)值賦值給lt3,也就是覆蓋lt3中原來的數(shù)據(jù),結(jié)果會編譯報(bào)錯
        // 報(bào)錯原因是集合中支持的類型不同
        // lt3 = lt1; Error
    }
}

結(jié)果為:

lt1 = [one]
獲取到的元素是:one
============================================================
lt2 = [1, 2]
獲取到的元素是:1
============================================================

底層原理

  • 泛型的本質(zhì)就是參數(shù)化類型,也就是讓數(shù)據(jù)類型作為參數(shù)傳遞,其中E相當(dāng)于形式參數(shù)負(fù)責(zé)占位,而使用集合時<>中的數(shù)據(jù)類型相當(dāng)于實(shí)際參數(shù),用于給形式參數(shù)E進(jìn)行初始化,從而使得集合中所有的E被實(shí)際參數(shù)替換,由于實(shí)際參數(shù)可以傳遞各種各樣廣泛的數(shù)據(jù)類型,因此得名為泛型。
  • 如:
其中i叫做形式參數(shù),負(fù)責(zé)占位 其中E叫做形式參數(shù),負(fù)責(zé)占位
int i = 10;int i = 20; E = String; E = Integer;
public static void show(int i) {...} public interface List<E> {...}
其中10叫做實(shí)際參數(shù),負(fù)責(zé)給形式參數(shù)初始化 其中String叫做實(shí)際參數(shù)
show(10); List<String> lt1 = ...;
show(20); List<String> lt2 = ...;

自定義泛型接口

  • 泛型接口和普通接口的區(qū)別就是后面添加了類型參數(shù)列表,可以有多個類型參數(shù),如:<E, T, .. >等。

自定義泛型類

  • 泛型類和普通類的區(qū)別就是類名后面添加了類型參數(shù)列表,可以有多個類型參數(shù),如:<E, T, .. >等。
  • 實(shí)例化泛型類時應(yīng)該指定具體的數(shù)據(jù)類型,并且是引用數(shù)據(jù)類型而不是基本數(shù)據(jù)類型。
  • 父類有泛型,子類可以選擇保留泛型也可以選擇指定泛型類型。
  • 子類必須是“富二代”,子類除了指定或保留父類的泛型,還可以增加自己的泛型。

例子:
先定義泛型類:

/**
 * 自定義泛型類Person,其中T相當(dāng)于形式參數(shù)負(fù)責(zé)占位,具體數(shù)值由實(shí)參決定
 * @param <T> 看做是一種名字為T的數(shù)據(jù)類型即可
 */
public class Person<T> {
    private String name;
    private int age;
    private T gender;
    
    public Person() {
    }
    
    public Person(String name, int age, T gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }
    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;
    }
    public T getGender() {
        return gender;
    }
    public void setGender(T gender) {
        this.gender = gender;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender=" + gender +
                '}';
    }
}

測試類:

public class PersonTest {
    public static void main(String[] args) {
        // 1.聲明Person類型的引用指向Person類型的對象
        Person p1 = new Person("zhangfei", 30, "男");
        // 2.打印對象的特征
        System.out.println(p1);

        System.out.println("============================================================");

        // 3.在創(chuàng)建對象的同時指定數(shù)據(jù)類型,用于給T進(jìn)行初始化
        Person<String> p2 = new Person<>();
        p2.setGender("女");
        System.out.println(p2);

        // 4.使用Boolean了類型作為性別的類型
        Person<Boolean> p3 = new Person<>();
        p3.setGender(true);
        System.out.println(p3);
    }
}

結(jié)果為:

Person{name='zhangfei', age=30, gender=男}
============================================================
Person{name='null', age=0, gender=女}
Person{name='null', age=0, gender=true}

泛型類被繼承時的處理方式
子類繼承是有四種繼承方式的
定義泛型類的子類:

public class SubPerson extends Person{// 不保留泛型而且沒有指定類型,此時Person類中的T默認(rèn)為是Object類型 擦除
}

測試會發(fā)現(xiàn)子類調(diào)用set方法時需要傳入的參數(shù)為Object類型
public class SubPerson extends Person<String>{// 不保留類型但是指定了泛型的類型,此時Person類型中的T被指定為String類型
}

此時發(fā)現(xiàn)子類調(diào)用set方法時需要傳入的參數(shù)為String類型
public class SubPerson<T> extends Person<T> {// 保留父類的泛型,可以在構(gòu)造對象時來指定T的類型
}
public class SubPerson<T, T1> extends Person<T> {// 保留父類的泛型,同時在子類中增加新的泛型
}

自定義泛型方法

  • 泛型方法就是我們輸入?yún)?shù)的時候,輸入的是泛型參數(shù),而不是具體的參數(shù)。我們在調(diào)用這個泛型 方法的時需要對泛型參數(shù)進(jìn)行實(shí)例化。
  • 泛型方法的格式:
    [訪問權(quán)限] <泛型> 返回值類型 方法名([泛型標(biāo)識 參數(shù)名稱]) { 方法體; }
  • 在靜態(tài)方法中使用泛型參數(shù)的時候,需要我們把靜態(tài)方法定義為泛型方法。

像這種不是泛型方法:

public T getGender() {
    return gender;
}

像這種才是泛型方法(在Person類中新定義的方法):

// 自定義方法實(shí)現(xiàn)將參數(shù)指定數(shù)組中的所有元素打印出來
public <T1> void printArray(T1[] arr) {
    for (T1 t : arr) {
        System.out.println("t = " + t);
    }
}

然后調(diào)用泛型方法:

// 5.調(diào)用泛型方法進(jìn)行測試
Integer[] arr = {11, 22, 33};
Person.printArray(arr);

結(jié)果為:

t = 11
t = 22
t = 33

一種特殊情況:

// 不是泛型方法,該方法不能使用static關(guān)鍵字修飾,因?yàn)樵摲椒ㄖ械腡需要在new對象時才能明確類型
public /*static*/ T getGender() {
    return gender;
}

泛型在繼承上的體現(xiàn)

  • 如果B是A的一個子類或子接口,而G是具有泛型聲明的類或接口,則G<B>并不是G<A>的子類型!比如:String是Object的子類,但是List<String>并不是List<Object>的子類。

通配符的使用

  • 有時候我們希望傳入的類型在一個指定的范圍內(nèi),此時就可以使用泛型通配符了。
  • 如:之前傳入的類型要求為Integer類型,但是后來業(yè)務(wù)需要Integer的父類Number類也可以傳入。
  • 泛型中有三種通配符形式:
    <?> 無限制通配符:表示我們可以傳入任意類型的參數(shù)。
    <? extends E> 表示類型的上界是E,只能是E或者是E的子類。
    <? super E> 表示類型的下界是E,只能是E或者是E的父類。

例子:
先定義父類

public class Animal {
}

再定義子類,繼承父類

public class Dog extends Animal{
}

測試類

public class GenericTest {
    public static void main(String[] args) {
        // 1.聲明兩個List類型的集合進(jìn)行測試
        List<Animal> lt1 = new LinkedList<>();
        List<Dog> lt2 = new LinkedList<>();
        // 試圖將lt2的數(shù)值賦值給lt1,也就是發(fā)生List<Dog>類型向List<Animal>類型的轉(zhuǎn)換
        //lt1 = lt2;  Error: 類型之間不具備父子類關(guān)系

        System.out.println("============================================================");

        // 2.使用通配符作為泛型類型的公共父類
        List<?> lt3 = new LinkedList<>();
        // 沒報(bào)錯,說明可以發(fā)生List<Animal>類型到List<?>類型的轉(zhuǎn)換
        lt3 = lt1;
        // 可以發(fā)生List<Dog>類型到List<?>類型的轉(zhuǎn)換
        lt3 = lt2;

        // 向公共父類中添加元素和獲取元素
        // lt3.add(new Animal()); Error: 不能存放Animal類型的對象
        // lt3.add(new Dog());    Error: 不能存放Dog類型的對象,不支持元素的添加操作,因?yàn)?可以是任意類型

        // 是可以的,支持元素的獲取操作,全部當(dāng)做Object類型來處理
        Object o = lt3.get(0);

        System.out.println("============================================================");

        // 3.使用有限制的通配符進(jìn)行使用
        List<? extends Animal> lt4 = new LinkedList<>();
        // 不支持元素的添加操作,因?yàn)?可以是Animal以及任意Animal的子類類型
        // 如果?為Dog類型,那么添加new Animal()對象就是錯誤的
        //lt4.add(new Animal());
        //lt4.add(new Dog());
        //lt4.add(new Object());
        // 獲取元素是可以的,因?yàn)槿〕鰜淼闹荒苁茿nimal或者Animal的子類
        Animal animal = lt4.get(0);

        System.out.println("============================================================");

        List<? super Animal> lt5 = new LinkedList<>();
        lt5.add(new Animal());
        lt5.add(new Dog());
        //lt5.add(new Object());  Error: 超過了Animal類型的范圍
        // 只有Object才能通用的處理
        Object object = lt5.get(0);
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容