最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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。

舉個栗子

假設我們有這樣一個需求:用魚缸養(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 "在玻璃魚缸里";
    }
}

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

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)建一個負責實例化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 "在玻璃魚缸里";
    }
}

結果如下:

在玻璃魚缸里金魚優(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的操作,只不過實例化的對象不同罷了。這就是典型的模板代碼,隨著魚缸越來越多,寫這些模板代碼浪費的時間也就越來越多。其實一開始崩潰的時候你的第一反應就是在FishTank里面實例化fish,但是又發(fā)現(xiàn)fish是泛型類型,無法實例化,沒辦法只能通過抽象方法交給子類去實例化,也就產(chǎn)生了無用的模板代碼。

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

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

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

如何解決

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

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

運行結果:

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強轉為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)雅的游動
金魚很漂亮

灰常完美~

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

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

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

相關文章

  • 詳細解讀spring中的@Resource注解

    詳細解讀spring中的@Resource注解

    這篇文章主要介紹了詳細解讀spring中的@Resource注解,此注解來源于JSR規(guī)范(Java?Specification?Requests),其作用是找到依賴的組件注入到應用來,它利用了JNDI技術查找所需的資源,需要的朋友可以參考下
    2023-10-10
  • 基于java 線程的幾種狀態(tài)(詳解)

    基于java 線程的幾種狀態(tài)(詳解)

    下面小編就為大家?guī)硪黄趈ava 線程的幾種狀態(tài)(詳解)。小編覺得挺不錯的,現(xiàn)在就想給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09
  • Java 字節(jié)數(shù)組(byte[])和整型(int)的相互轉換

    Java 字節(jié)數(shù)組(byte[])和整型(int)的相互轉換

    在Java編程中,有時需要將字節(jié)類型(byte)轉換為整數(shù)類型(int),或者反過來轉換,本文主要介紹了Java 字節(jié)數(shù)組(byte[])和整型(int)的相互轉換,感興趣的可以了解一下
    2023-12-12
  • SpringBoot獲取maven打包時間的兩種方式

    SpringBoot獲取maven打包時間的兩種方式

    這篇文章主要介紹了SpringBoot獲取maven打包時間的兩種方式,文章通過代碼示例給大家講解的非常詳細,對大家的學習或工作有一定的幫助,需要的朋友可以參考下
    2024-05-05
  • 關于mybatis callSettersOnNulls 配置解析

    關于mybatis callSettersOnNulls 配置解析

    這篇文章主要介紹了關于mybatis callSettersOnNulls 配置,非常不錯,具有一定的參考借鑒價值 ,需要的朋友可以參考下
    2018-06-06
  • SpringBoot Actuator監(jiān)控的項目實踐

    SpringBoot Actuator監(jiān)控的項目實踐

    本文主要結合 Spring Boot Actuator,跟大家一起分享微服務Spring Boot Actuator 的常見用法,方便我們在日常中對我們的微服務進行監(jiān)控治理,感興趣的可以了解一下
    2024-01-01
  • SpringBoot整合flyway實現(xiàn)自動創(chuàng)建表的方法

    SpringBoot整合flyway實現(xiàn)自動創(chuàng)建表的方法

    這篇文章主要介紹了SpringBoot整合flyway實現(xiàn)自動創(chuàng)建表的方法,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-03-03
  • Spring Data JPA使用Sort進行排序(Using Sort)

    Spring Data JPA使用Sort進行排序(Using Sort)

    本篇文章主要介紹了Spring Data JPA使用Sort進行排序(Using Sort),具有一定的參考價值,有興趣的可以了解一下
    2017-07-07
  • 關于Spring?Validation數(shù)據(jù)校檢的使用流程分析

    關于Spring?Validation數(shù)據(jù)校檢的使用流程分析

    在實際項目中,對客戶端傳遞到服務端的參數(shù)進行校驗至關重要,SpringValidation提供了一種便捷的方式來實現(xiàn)這一需求,通過在POJO類的屬性上添加檢查注解,本文給大家介紹Spring?Validation數(shù)據(jù)校檢的使用流程,感興趣的朋友一起看看吧
    2024-11-11
  • 解決項目版本沖突之maven-shade插件使用方式

    解決項目版本沖突之maven-shade插件使用方式

    本文介紹了在Maven項目中如何處理三方依賴與已有依賴沖突的問題,使用maven-shade-plugin插件實現(xiàn)依賴隔離,同時避免版本不一致導致項目異常,通過重命名依賴包名實現(xiàn)不同版本共存,解決三方依賴版本不一致問題,另外還提到了在使用過程中一些坑點和解決方案
    2026-05-05

最新評論

武邑县| 砚山县| 北宁市| 平邑县| 河南省| 进贤县| 孟津县| 郎溪县| 来凤县| 河南省| 北票市| 高碑店市| 昌吉市| 彰化县| 苍梧县| 密云县| 罗甸县| 出国| 周至县| 三明市| 九寨沟县| 蒙山县| 涞源县| 海兴县| 红桥区| 成武县| 五家渠市| 广水市| 根河市| 丹棱县| 弥勒县| 桑日县| 吉首市| 泰和县| 木兰县| 吉林市| 丰台区| 普定县| 五莲县| 德州市| 甘肃省|