欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java使用泛型Class實現(xiàn)消除模板代碼

 更新時間:2023年06月02日 10:57:27   作者:EsanWoo  
Class作為實現(xiàn)反射功能的類,在開發(fā)中經(jīng)常會用到,然而,當Class遇上泛型后,事情就變得不是那么簡單了,所以本文就來講講Java如何使用泛型Class實現(xiàn)消除模板代碼,需要的可以參考一下

Class作為實現(xiàn)反射功能的類,在開發(fā)中經(jīng)常會用到,最常見的就是啟動一個Activity: startActivity(new Intent(this,AnotherActivity.class))。眾所周知,獲取某一個類的Class有如下三種方法:

Class d1 = Demo.class;
Class d2 = new Demo().getClass();
Class d3 = Class.forName("com.xxx.xxx.Demo);

然而,當Class遇上泛型后,事情就變得不是那么簡單了。當你需要使用泛型的Class時,你可能會略微感到一絲蛋疼:無法通過上面三種方法獲取到泛型的Class,因為泛型它不是一個確定的具體的類,自然就無法通過上面方法獲取到Class。

舉個栗子

假設(shè)我們有這樣一個需求:用魚缸養(yǎng)魚。首先定義一個接口魚:

interface Fish {
    String swim();    //魚的通用動作游泳
}

魚有很多種類,我們創(chuàng)建兩種魚:

public class GoldFish implements Fish{
    @Override
    public String swim() {
        return "金魚優(yōu)雅的游動";
    }
    public void beautiful(){
        System.out.println("金魚很漂亮");    //金魚特有的屬性
    }
}
//魚可殺不可辱,兄弟你可以釣我但你不能侮辱我是草魚
public class GrassFish implements Fish{
    @Override
    public String swim() {
        return "細紋獅子魚迅速的游動";
    }
    public void agile(){
        System.out.println("細紋獅子魚非常敏捷");    //細紋獅子魚特有的屬性
    }
}

好魚有了,下面我們來創(chuàng)建魚缸,魚缸也有不同材質(zhì)的,所以將魚缸定義為抽象類,并且創(chuàng)建抽象方法abstract String material()

public abstract class FishTank<F extends Fish> {
    F fish;
    public FishTank() {
        show();
    }
    private void show(){
        System.out.println(material() + fish.swim());
    }
    abstract String material();
}

魚缸里要有魚,但是魚缸里又不一定會養(yǎng)什么魚,因此為魚缸添加了泛型<F extends Fish>,并聲明魚的變量F fish。

現(xiàn)在魚缸也有了,接下來我們買一個玻璃魚缸來養(yǎng)金魚:

//創(chuàng)建(買)一個玻璃魚缸,泛型傳入(養(yǎng))金魚
public class GlassGoldFishTank extends FishTank<GoldFish>{
    @Override
    public String material() {
        return "在玻璃魚缸里";
    }
}

將魚缸放進屋子中,我們來看一看結(jié)果:

public class Room {
    public static void main(String[] args) {
        new GlassFishTank();
    }
}

發(fā)現(xiàn)崩潰了...

Exception in thread "main" java.lang.NullPointerException
    at com.testapplication.FishTank.show(FishTank.java:10)
    at com.testapplication.FishTank.<init>(FishTank.java:6)
    at com.testapplication.GlassGoldFishTank.<init>(GlassGoldFishTank.java:3)
    at com.testapplication.Room.main(Room.java:13)

不慌,問題不大,原因很簡單,變量fish沒有實例化,實例化一下即可。但此時你會發(fā)現(xiàn),無法在FishTank中實例化fish,因為變量fish的類型是泛型。嗯..問題也不大,創(chuàng)建一個負責(zé)實例化fish的抽象方法abstract void initFish()去交給子類實現(xiàn)即可:

    public abstract class FishTank<F extends Fish> {
        F fish;
        public FishTank() {
            initFish();
            show();
        }
        private void show(){
            System.out.println(material() + fish.swim());
        }
        abstract void initFish();
        abstract String material();
    }
    public class GlassGoldFishTank extends FishTank<GoldFish>{
        @Override
        public void initFish(){
            fish = new GoldFish();
        }
        @Override
        public String material() {
            return "在玻璃魚缸里";
        }
}

ok,再來試一下:

在玻璃魚缸里金魚優(yōu)雅的游動

我們還可以展示一下金魚特有的屬性:

public class GlassGoldFishTank extends FishTank<GoldFish>{
    public GlassGoldFishTank() {
        fish.beautiful();
    }
    @Override
    void initFish() {
        fish = new GoldFish();
    }
    @Override
    String material() {
        return "在玻璃魚缸里";
    }
}

結(jié)果如下:

在玻璃魚缸里金魚優(yōu)雅的游動
金魚很漂亮

簡直完美,無敵~

過了兩天,你看金魚看夠了,又買了個玻璃魚缸,這次養(yǎng)的是草魚

//創(chuàng)建(買)一個玻璃魚缸,泛型傳入(養(yǎng))細紋獅子魚
public class GlassGrassFishTank extends FishTank<GrassFish>{
    public GlassGrassFishTank() {
        fish.alige();
    }
    @Override
    void initFish() {
        fish = new GrassFish();
    }
    @Override
    String material() {
        return "在玻璃魚缸里";
    }
}
//放入屋子中:
public class Room {
    public static void main(String[] args) {
        new GlassGoldFishTank();
        new GlassGrassFishTank();
    }
}

試下效果:

在玻璃魚缸里金魚優(yōu)雅的游動
金魚很漂亮
在玻璃魚缸里細紋獅子魚迅速的游動
細紋獅子魚非常敏捷

完美~

又過兩天,你又看夠了,這回你買了個水晶魚缸來養(yǎng)金魚:

public class CrystalGoldFishTank extends FishTank<GoldFish>{
    ...
    @Override
    void initFish() {
        fish = new GoldFish();
    }
    ...
}

又過兩天,你又看夠了,又買了個鉆石魚缸來養(yǎng)草魚:

public class DiamondGrassFishTank extends FishTank<GrassFish>{
        ...
        @Override
        void initFish() {
            fish = new GrassFish();
        }
        ...
    }

一個問題

不知道你有沒有發(fā)現(xiàn),每買一個魚缸,就要實現(xiàn)一下initFish()方法,但是這個方法里面的代碼功能是一樣的,每個魚缸的initFish()方法里面都是做的實例化fish的操作,只不過實例化的對象不同罷了。這就是典型的模板代碼,隨著魚缸越來越多,寫這些模板代碼浪費的時間也就越來越多。其實一開始崩潰的時候你的第一反應(yīng)就是在FishTank里面實例化fish,但是又發(fā)現(xiàn)fish是泛型類型,無法實例化,沒辦法只能通過抽象方法交給子類去實例化,也就產(chǎn)生了無用的模板代碼。

其實不光是這個例子,在實際開發(fā)中也會遇到這種情況,比如我們在封裝BaseActivity的時候,如果項目使用MVVM架構(gòu),那么BaseActivity里就要持有ViewModel,因此就要給BaseActivity加上ViewModel的泛型:

public abstract class BaseActivity<VM extends BaseViewModel>{
    protected VM mViewModel;
    ...
}

ViewModel需要實例化,但是它的類型卻是個泛型,那一些經(jīng)驗不夠豐富的童鞋可能就不知道如何在BaseActivity中進行實例化,就只能創(chuàng)建抽象方法abstract void initViewModel()在具體子類業(yè)務(wù)Activity中去進行實例化,這就跟上面例子中的實例化fish一樣是模板代碼了。

如何解決

那到底有沒有辦法在基類中實例化泛型呢?答案是有的,就是使用Class類中的getGenericSuperclass()方法。這個方法的作用是獲取該類的帶有準確泛型類型的父類,它與getSuperclass()方法的區(qū)別是getSuperclass方法返回的是寬泛的父類類型,不包含具體泛型信息。如果該類的父類不是參數(shù)化類型(即沒有泛型),那么這兩個方法的效果是一樣的。光說不容易理解,上代碼:

public class GlassFishTank extends FishTank<GrassFish>{
    @RequiresApi(api = Build.VERSION_CODES.P)
    public GlassFishTank() {
        //在構(gòu)造方法中打印兩個方法獲取到的類
        print(getClass().getGenericSuperclass().getTypeName());
        print(getClass().getSuperclass().getTypeName());
    }
}

運行結(jié)果:

com.testapplication.FishTank<com.testapplication.GrassFish>
com.testapplication.FishTank

可以看到,getGenericSuperclass方法獲取到了代碼運行時FishTank類的泛型的準確類型,即GrassFish。既然知道了準確的泛型類型了,那么就可以獲取到泛型的Class,獲取到了泛型的Class,自然就可以實例化了。話不多說,直接上代碼:

public abstract class FishTank<F extends Fish> {
    F fish;
    @RequiresApi(api = Build.VERSION_CODES.P)
    public FishTank() {
        initFish();
    }
    private void initFish(){
        // ① 獲取該類的帶有準確泛型類型的父類
        Type genericSuperclass = getClass().getGenericSuperclass();
        // ② 判斷父類是否是參數(shù)化類型。由于本例中父類必定是參數(shù)化類型,因此也可以去掉②③步,在①中直接聲明變量為ParameterizedType類型
        if (genericSuperclass instanceof ParameterizedType){
            // ③ 如果是參數(shù)化類型,就將genericSuperclass強轉(zhuǎn)為ParameterizedType
            ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
            // ④ 獲取泛型類型的Class對象數(shù)組。因為可能有多個泛型,所以返回的是數(shù)組
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            //本例中只有一個泛型,數(shù)組中只有一個元素,所以直接取數(shù)組的第一個元素即可
            Class<F> fishClass = (Class<F>) actualTypeArguments[0];
            try {
                // ⑤ 實例化fish變量
                fish = fishClass.newInstance();
                show();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    private void show(){
        print(material() + fish.swim());
    }
    abstract String material();
}

如此一來,實例化fish的代碼就只需在FishTank中寫一次,無需在每個魚缸中都實例化fish了,玻璃魚缸現(xiàn)在變成了這樣:

public class GlassFishTank extends FishTank<GrassFish>{
    public GlassFishTank() {
        fish.agile();
    }
    @Override
    String material() {
        return "在玻璃魚缸里";
    }
}

試一下效果:

在玻璃魚缸里細紋獅子魚迅速的游動
細紋獅子魚非常敏捷

完美~

甚至還可以再簡潔一點,將魚特有的屬性也放入FishTank中:

public abstract class FishTank<F extends Fish> {
                ...
                try {
                    // ⑤ 實例化fish變量
                    fish = fishClass.newInstance();
                    show();
                    // ⑥ 獲取fish類中的所有方法并遍歷
                    Method[] methods = fishClass.getMethods();
                    for (int i = 0; i < methods.length; i++) {
                        //如果該方法是fish類中的方法(為了過濾掉父類中的方法)并且返回值類型為viod(為了過濾掉swim方法),說明是不同種類的魚特有的屬性,就執(zhí)行該方法
                        if (methods[i].getDeclaringClass() == fishClass && methods[i].getReturnType() == void.class)
                            methods[i].invoke(fish);
                    }
                }
        private void show(){
            print(material() + fish.swim());
        }
        abstract String material();
    }

此時玻璃魚缸變成了這樣:

public class GlassFishTank extends FishTank<GoldFish>{
    @Override
    String material() {
        return "在玻璃魚缸里";
    }
}

是不是非常簡潔了,想養(yǎng)其他魚只需修改一下泛型類型即可,完全不需要做其他任何操作??匆幌聢?zhí)行效果:

在玻璃魚缸里金魚優(yōu)雅的游動
金魚很漂亮

灰常完美~

我們也可以將這個技巧應(yīng)用到BaseActivity中,這樣就不用每個具體業(yè)務(wù)Activity中都要寫實例化ViewModel的模板代碼了,你的代碼是不是變得更優(yōu)雅了呢~

這只是一個小技巧,并不是什么很牛逼的技術(shù),只不過上面幾個方法你可能平時很少用到或者根本沒用過,不知道有這幾個方法,當然也就不知道可以這么實現(xiàn)了。所以我們在鉆研技術(shù)的深度時,也不要太忽視技術(shù)的廣度,畢竟地基打的牢,房子才能蓋的結(jié)實漂亮。

到此這篇關(guān)于Java使用泛型Class實現(xiàn)消除模板代碼的文章就介紹到這了,更多相關(guān)Java泛型Class內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java并發(fā)編程之LongAdder執(zhí)行情況解析

    Java并發(fā)編程之LongAdder執(zhí)行情況解析

    這篇文章主要為大家介紹了Java并發(fā)編程之LongAdder執(zhí)行情況解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-04-04
  • 深入解析java HashMap實現(xiàn)原理

    深入解析java HashMap實現(xiàn)原理

    這篇文章主要介紹了深入解析java HashMap實現(xiàn)原理的相關(guān)資料,需要的朋友可以參考下
    2015-09-09
  • Java實現(xiàn)訂單超時自動取消的7種方案

    Java實現(xiàn)訂單超時自動取消的7種方案

    在電商、外賣、票務(wù)等系統(tǒng)中,訂單超時未支付自動取消是一個常見的需求,這個功能乍一看很簡單,甚至很多初學(xué)者會覺得:"不就是加個定時器么?" 但真到了實際工作中,細節(jié)的復(fù)雜程度往往會超乎預(yù)期,本文給大家介紹了Java實現(xiàn)訂單超時自動取消的7種方案
    2024-12-12
  • Java中的轉(zhuǎn)換流、壓縮流、序列化流、打印流及應(yīng)用場景

    Java中的轉(zhuǎn)換流、壓縮流、序列化流、打印流及應(yīng)用場景

    這篇文章主要介紹了Java中的轉(zhuǎn)換流、壓縮流、序列化流、打印流及應(yīng)用場景,本文結(jié)合示例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-06-06
  • 200行Java代碼如何實現(xiàn)依賴注入框架詳解

    200行Java代碼如何實現(xiàn)依賴注入框架詳解

    依賴注入對大家來說應(yīng)該都不陌生,下面這篇文章主要給大家介紹了關(guān)于利用200行Java代碼如何實現(xiàn)依賴注入框架的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-05-05
  • Java中的CompletableFuture原理與用法

    Java中的CompletableFuture原理與用法

    CompletableFuture 是由Java8引入的,這讓我們編寫清晰可讀的異步代碼變得更加容易,該類功能比Future 更加強大,在Java中CompletableFuture用于異步編程,異步通常意味著非阻塞,運行任務(wù)單獨的線程,與主線程隔離,這篇文章介紹CompletableFuture原理與用法,一起看看吧
    2024-01-01
  • SecurityUtils.getSubject().getPrincipal()為null的問題

    SecurityUtils.getSubject().getPrincipal()為null的問題

    這篇文章主要介紹了SecurityUtils.getSubject().getPrincipal()為null的問題及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • java中Class類的基礎(chǔ)知識點及實例

    java中Class類的基礎(chǔ)知識點及實例

    在本篇文章里小編給大家分享了關(guān)于java中Class類的基礎(chǔ)知識點及實例內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。
    2021-05-05
  • 關(guān)于Springboot數(shù)據(jù)庫配置文件明文密碼加密解密的問題

    關(guān)于Springboot數(shù)據(jù)庫配置文件明文密碼加密解密的問題

    這篇文章主要介紹了Springboot數(shù)據(jù)庫配置文件明文密碼加密解密的問題,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-03-03
  • 詳解Spring Data JPA系列之投影(Projection)的用法

    詳解Spring Data JPA系列之投影(Projection)的用法

    本篇文章主要介紹了詳解Spring Data JPA系列之投影(Projection)的用法,具有一定的參考價值,有興趣的可以了解一下
    2017-07-07

最新評論