激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|JavaScript|易語言|

服務器之家 - 編程語言 - JAVA教程 - Java實例化一個抽象類對象的方法教程

Java實例化一個抽象類對象的方法教程

2021-03-04 09:51twiceyuan JAVA教程

大家都知道抽象類無法實例化,就無法創建對象。所以下面這篇文章主要給大家介紹了關于Java實例化一個抽象類對象的相關資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面隨著小編來一起學習學習吧。

前言

最近在學習的過程中,發現了一個問題,抽象類在沒有實現所有的抽象方法前是不可以通過new來構建該對象的,但是抽象方法卻是可以有自己的構造方法的。這樣就把我搞糊涂了,既然有構造方法,又不可以通過new來創建,那么抽象類在沒變成具體類的時候究竟可不可以實例化呢?

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

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

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

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

?
1
2
3
4
5
6
7
8
9
public interface RealApi {
 @GET("api1")
 Observable<String> api1();
 @GET("api2")
 Observable<String> api2();
 @GET("api3")
 Observable<String> api3();
 //...其他方法
}

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

?
1
2
3
4
5
6
@MockApi
public abstract class MockApi implements RealApi {
 Observable<String> api3() {
 return Observable.just("mock data");
 }
}

然后我們需要有一個工具,例如 MockManager ,讓他結合我們已存在的 RealApi 對象和 MockApi 類,來構造出一個混合對象,該對象在執行 MockApi 中已經定義的方法時,為直接執行,在 MockApi 沒有定義該方法時,去調用 RealApi 的方法。其調用方式大概為:

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

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

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

?
1
2
3
4
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 上的虛擬機的實現和標準略微不同,所以這里把方向轉為了動態代碼生成的另一個方向 Annotation Processor。

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

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

?
1
2
3
4
5
@Target(ElementType.TYPE)
@Documented
@Retention(RetentionPolicy.SOURCE)
public @interface MockApi {
}

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

?
1
2
3
4
5
6
7
8
9
10
11
// 生成的類名使用原類名+"$Impl"的后綴來命名,避免和其他類名沖突,后面也使用該約束進行反射來調用該類
public final class MockApi$Impl extends MockApi {
 @Override
 public Observable<String> api1() {
 throw new IllegalStateException("api1() is an abstract method!");
 }
 @Override
 public Observable<String> api2() {
 throw new IllegalStateException("api2() is an abstract method!");
 }
}

根據該抽象類的類名去反射獲得該實現類,然后再根據反射調用其構造方法構造出一個實現對象。

?
1
2
3
4
5
6
7
8
// 獲得生成代碼構造的對象
private static <T> T getImplObject(Class<T> cls) {
 try {
 return (T) Class.forName(cls.getName() + "$Impl").newInstance();
 } catch (Exception e) {
 return null;
 }
}

構造一個動態代理,傳入 RealApi 的真實對象,和上一步構造出的抽象類的實現對象,根據抽象類中的定義來判斷由哪個對象代理其方法行為:如果抽象類中有定義,即該方法不是抽象方法,則抽象類的實現對象執行;反之,由接口的真實對象執行。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static <Origin, Mock extends Origin> Origin build(final Origin origin, final Class<Mock> mockClass) {
 // 如果 Mock Class 標記為關閉,則直接返回真實接口對象
 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 { 
  // 獲取定義的抽象類中的同名方法,判斷是否已經實現
  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 方法來構造一個混合了真實接口和抽象類方法的代理對象了,雖然調用的類本質上還是硬編碼,但是由 Annotation Processor 自動生成免于手動維護,使用上來講和使用 Javassist 實現還是基本相同的。

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

文中提到的源碼實現可以在項目 retrofit-mock-result 中找到;

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。

原文鏈接:https://twiceyuan.com/2017/04/07/instantiating-an-abstract-class-in-java/

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 亚洲第一成人在线视频 | 精品国产三级a | 国产自在线| 九九热视频免费观看 | 成人在线网站 | 中国嫩模一级毛片 | 国产手机在线视频 | 国产精品热 | 国产资源在线播放 | 中文字幕免费播放 | 欧美一级特黄a | 污黄视频在线观看 | 97黄色网 | 一本色道久久综合亚洲精品图片 | 在线看一区二区三区 | 娇喘在线 | 国产一级免费在线视频 | 黄色大片大毛片 | 意大利av在线 | 午夜生活理论片 | 欧美一级爱爱 | 免费黄色在线 | 激情综合网俺也去 | 国产精品999在线观看 | 爱操视频 | chinesegv男男猛男无套 | 蜜桃传免费看片www 日本一区二区三区视频在线 | 精品国产96亚洲一区二区三区 | 欧美一级毛片特黄黄 | 亚洲精品tv久久久久久久久久 | 在线2区| 亚洲无毛av | av电影网在线观看 | 久久久鲁| 黄色免费网站在线观看 | av电影免费播放 | 亚洲国产精品二区 | 污黄视频在线播放 | 久久99网| 羞羞网站在线观看入口免费 | 草久影视|