1. <ul id="0c1fb"></ul>

      <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
      <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区

      RELATEED CONSULTING
      相關(guān)咨詢
      選擇下列產(chǎn)品馬上在線溝通
      服務(wù)時間:8:30-17:00
      你可能遇到了下面的問題
      關(guān)閉右側(cè)工具欄

      新聞中心

      這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
      Java如何實例化一個抽象類對象-創(chuàng)新互聯(lián)

      這篇文章主要介紹Java如何實例化一個抽象類對象,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

      目前成都創(chuàng)新互聯(lián)公司已為千余家的企業(yè)提供了網(wǎng)站建設(shè)、域名、網(wǎng)站空間網(wǎng)站托管運營、企業(yè)網(wǎng)站設(shè)計、漢川網(wǎng)站維護等服務(wù),公司將堅持客戶導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長,共同發(fā)展。

      在Java 中抽象類是不能直接被實例化的。但是很多時候抽象類的該特點成為一個比較麻煩的阻礙。例如如果我想使用動態(tài)代理來給一個抽象類賦予其執(zhí)行抽象方法的能力,就會有兩個困難:1. 動態(tài)代理只能創(chuàng)建實現(xiàn)接口的一個代理對象,而不能是一個繼承抽象類的對象。為此標準的 JVM 中有一些實現(xiàn),例如 javassist 可以使用字節(jié)碼工具來完成這一目的(ProxyFactory)。

      在 Android 中如果想構(gòu)造一個抽象類對象,恐怕只有 new ClassName() {} 或者繼承之后構(gòu)造了。但是這兩種方法都是不能由其 Class 對象直接操作的,這就導(dǎo)致一些問題上達不到我們需要的抽象能力。

      這里詳細描述一下第一段所說的場景:

      首先有一個 interface 文件定義如下(熟悉 Android 的朋友可以看出這是一個提供給 Retrofit 生成代理對象的 Api 配置接口):

      public interface RealApi { 
       @GET("api1")
       Observable api1(); 
       @GET("api2")
       Observable api2(); 
       @GET("api3")
       Observable api3();
       //...其他方法
      }

      其次再寫一個抽象類,只實現(xiàn)接口的其中一個方法(用來模擬接口數(shù)據(jù)):

      @MockApi
      public abstract class MockApi implements RealApi {
       Observable api3() {
       return Observable.just("mock data");
       }
      }

      然后我們需要有一個工具,例如 MockManager ,讓他結(jié)合我們已存在的 RealApi 對象和 MockApi 類,來構(gòu)造出一個混合對象,該對象在執(zhí)行 MockApi 中已經(jīng)定義的方法時,為直接執(zhí)行,在 MockApi 沒有定義該方法時,去調(diào)用 RealApi 的方法。其調(diào)用方式大概為:

      RealApi api = MockManager.build(realApi, MockApi.class);

      通過 javassist,完成上述功能很簡單,創(chuàng)建一個 ProxyFactory 對象,設(shè)置其 Superclass 為MockApi,然后過濾抽象方法,設(shè)置 method handler 調(diào)用 realApi 對象的同名同參方法。這里就不再給出代碼實現(xiàn)。

      但是在 Android 上,javassist 的該方法會拋出

      Caused by: java.lang.UnsupportedOperationException: can't load this type of class file 
        at java.lang.ClassLoader.defineClass(ClassLoader.java:520)
        at java.lang.reflect.Method.invoke(Native Method)
        at javassist.util.proxy.FactoryHelper.toClass2(FactoryHelper.java:182)

      類似的異常。原因大概是 Android 上的虛擬機的實現(xiàn)和標準略微不同,所以這里把方向轉(zhuǎn)為了動態(tài)代碼生成的另一個方向 Annotation Processor。

      使用 Annotation Processor 實現(xiàn)的話,思路就簡單的多了,但過程還是有些曲折:

      首先定義一個注解,用來標記需要構(gòu)造對象的抽象類

      @Target(ElementType.TYPE)
      @Documented
      @Retention(RetentionPolicy.SOURCE)
      public @interface MockApi {
      }

      Processor 根據(jù)注解來獲得類的 element 對象,該對象是一個類似 class 的對象。因為在預(yù)編譯階段,class 尚未存在,此時使用 Class.forName 是不可以獲取運行時需要的 Class 對象的,但是 Element 提供了類似 Class 反射相關(guān)的方法,也有 TypeElement、ExecutableElement 等區(qū)分。使用 Element 對象分析注解的抽象類的抽象方法有哪些,生成一個繼承該類的實現(xiàn)類(非抽象),并在該類中實現(xiàn)所有抽象方法,因為不會實際用到這些抽象方法,所以只需要能編譯通過就可以了,我選擇的方式是每個方法體都拋出一個異常,提示該方法為抽象方法不能直接調(diào)用。生成代碼的方法可以使用一些工具來簡化工作,例如 AutoProcessor 和 JavaPoet,具體實現(xiàn)參考文尾的項目代碼,生成后的代碼大致像這樣:

      // 生成的類名使用原類名+"$Impl"的后綴來命名,避免和其他類名沖突,后面也使用該約束進行反射來調(diào)用該類
      public final class MockApi$Impl extends MockApi {
       @Override
       public Observable api1() {
       throw new IllegalStateException("api1() is an abstract method!");
       }
       @Override
       public Observable api2() {
       throw new IllegalStateException("api2() is an abstract method!");
       }
      }

      根據(jù)該抽象類的類名去反射獲得該實現(xiàn)類,然后再根據(jù)反射調(diào)用其構(gòu)造方法構(gòu)造出一個實現(xiàn)對象。

      // 獲得生成代碼構(gòu)造的對象
      private static  T getImplObject(Class cls) {
       try {
       return (T) Class.forName(cls.getName() + "$Impl").newInstance();
       } catch (Exception e) {
       return null;
       }
      }

      構(gòu)造一個動態(tài)代理,傳入 RealApi 的真實對象,和上一步構(gòu)造出的抽象類的實現(xiàn)對象,根據(jù)抽象類中的定義來判斷由哪個對象代理其方法行為:如果抽象類中有定義,即該方法不是抽象方法,則抽象類的實現(xiàn)對象執(zhí)行;反之,由接口的真實對象執(zhí)行。

      public static  Origin build(final Origin origin, final Class mockClass) {
       // 如果 Mock Class 標記為關(guān)閉,則直接返回真實接口對象
       if (!isEnable(mockClass)) {
       return origin;
       }
       final Mock mockObject = getImplObject(mockClass);
       Class originClass = origin.getClass().getInterfaces()[0];
       return (Origin) Proxy.newProxyInstance(originClass.getClassLoader(), new Class[]{originClass}, new InvocationHandler() { 
       @Override
       public Object invoke(Object o, Method method, Object[] objects) throws Throwable {  
        // 獲取定義的抽象類中的同名方法,判斷是否已經(jīng)實現(xiàn)
        Method mockMethod = null;
        try {
        mockMethod = mockClass.getDeclaredMethod(method.getName(), method.getParameterTypes());
        } catch (NoSuchMethodException ignored) {
        }  
        if (mockMethod == null || Modifier.isAbstract(mockMethod.getModifiers())) {
        return method.invoke(origin, objects);
        } else {
        return mockMethod.invoke(mockObject, objects);
        }
       }
       });
      }

      完成上述工作以后,就可以像開頭所說的那樣,使用 build 方法來構(gòu)造一個混合了真實接口和抽象類方法的代理對象了,雖然調(diào)用的類本質(zhì)上還是硬編碼,但是由 Annotation Processor 自動生成免于手動維護,使用上來講和使用 Javassist 實現(xiàn)還是基本相同的。

      我用本文中所屬的方法實現(xiàn)了一個模擬 retrofit 請求的工具(文尾有鏈接),但本質(zhì)上可以用它來實現(xiàn)很多需要構(gòu)造抽象類的需求,更多的使用場景還有待挖掘。

      文中提到的源碼實現(xiàn)可以在項目 retrofit-mock-result 或本地下載中找到;

      以上是“Java如何實例化一個抽象類對象”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!


      分享標題:Java如何實例化一個抽象類對象-創(chuàng)新互聯(lián)
      文章地址:http://www.ef60e0e.cn/article/csdgss.html
      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区
      1. <ul id="0c1fb"></ul>

        <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
        <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

        禹城市| 若羌县| 榆社县| 平昌县| 襄樊市| 杂多县| 克什克腾旗| 平顺县| 普兰店市| 清镇市| 会东县| 会理县| 泾阳县| 绍兴市| 元氏县| 湾仔区| 杭锦旗| 凤翔县| 台山市| 临武县| 崇文区| 太谷县| 江都市| 靖西县| 洪泽县| 德钦县| 南宫市| 安达市| 北辰区| 辉县市| 温宿县| 镇原县| 马关县| 齐河县| 荔波县| 定结县| 基隆市| 安达市| 大化| 汨罗市| 台东市|