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

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

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

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

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

然而,當(dāng)Class遇上泛型后,事情就變得不是那么簡單了。當(dāng)你需要使用泛型的Class時(shí),你可能會(huì)略微感到一絲蛋疼:無法通過上面三種方法獲取到泛型的Class,因?yàn)榉盒退皇且粋€(gè)確定的具體的類,自然就無法通過上面方法獲取到Class。

舉個(gè)栗子

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

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

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

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

好魚有了,下面我們來創(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();
}

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

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

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

將魚缸放進(jìn)屋子中,我們來看一看結(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沒有實(shí)例化,實(shí)例化一下即可。但此時(shí)你會(huì)發(fā)現(xiàn),無法在FishTank中實(shí)例化fish,因?yàn)樽兞縡ish的類型是泛型。嗯..問題也不大,創(chuàng)建一個(gè)負(fù)責(zé)實(shí)例化fish的抽象方法abstract void initFish()去交給子類實(shí)現(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)雅的游動(dòng)

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

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

結(jié)果如下:

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

簡直完美,無敵~

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

//創(chuàng)建(買)一個(gè)玻璃魚缸,泛型傳入(養(yǎng))細(xì)紋獅子魚
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)雅的游動(dòng)
金魚很漂亮
在玻璃魚缸里細(xì)紋獅子魚迅速的游動(dòng)
細(xì)紋獅子魚非常敏捷

完美~

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

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

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

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

一個(gè)問題

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

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

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

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

如何解決

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

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

運(yùn)行結(jié)果:

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

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

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

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

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

試一下效果:

在玻璃魚缸里細(xì)紋獅子魚迅速的游動(dòng)
細(xì)紋獅子魚非常敏捷

完美~

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

public abstract class FishTank<F extends Fish> {
                ...
                try {
                    // ⑤ 實(shí)例化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();
    }

此時(shí)玻璃魚缸變成了這樣:

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

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

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

灰常完美~

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

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

到此這篇關(guān)于Java使用泛型Class實(shí)現(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í)行情況解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • 深入解析java HashMap實(shí)現(xiàn)原理

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

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

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

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

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

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

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

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

    Java中的CompletableFuture原理與用法

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

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

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

    java中Class類的基礎(chǔ)知識(shí)點(diǎn)及實(shí)例

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

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

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

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

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

最新評論