2016-01-10 100 views
8

我想在運行時爲使用Byte Buddy的抽象類創建一個實現,我面臨的問題是從創建的實例調用方法時拋出java.lang.AbstractMethodError 。我有一個現有的abstract類這樣的(這我其實不能修改和實際上包含更多的邏輯):Byte Buddy:爲抽象類創建實現

​​

使用下面的最少的樣品,我想我的Algorithm實例返回一個恆定值:

Class<?> type = new ByteBuddy() 
         .subclass(Algorithm.class) 
         .method(ElementMatchers.named("execute")) 
         .intercept(FixedValue.value(42)) 
         .make() 
         .load(classLoader, ClassLoadingStrategy.Default.WRAPPER) 
         .getLoaded(); 
Algorithm instance = (Algorithm) type.newInstance(); 
System.out.println(myInstance.execute()); 

然而,這會導致以下異常:

Exception in thread "main" java.lang.AbstractMethodError: package.Algorithm.execute()I 

(當我實驗改變Algorithm到一個interface,一切工作正常,但這並不能解決我的具體問題)。

回答

11

它可能會讓你大吃一驚,但如果使用相同的類加載器設置,使用javac生成相同的代碼,則會發生完全相同的事情。您所觀察到的隱含在JLS中如何指定包隱私。您非接口

public abstract class Algorithm { 
    abstract int execute(); 
} 

定義了一個包私有方法。由於您沒有爲生成的類定義自定義名稱,因此Byte Buddy將生成一個具有隨機名稱的子類,該子類位於相同的包中。 Byte Buddy進一步發現executable方法可以從生成的子類中覆蓋,並且完全按照您預期的方式實現它。

但是,您正在使用ClassLoadingStrategy.Default.WRAPPER策略來加載創建一個加載Algorithm的新子級加載器的類。在Java中,在運行時,但是兩個包僅在時相等,如果包的名稱相同且兩個包都由相同的ClassLoader加載。對於您的情況,後面的條件不適用,因此JVM不再將類型應用於execute類。通過調用

((Algorithm) type.newInstance()).execute(); 

因此,您不是調用生成的方法,而是調用原始的抽象方法。因此 - 根據JLS - 一個AbstractMethodError被拋出。

要解決這個問題,您可能需要加載在相同的包中生成的類,使用默認INJECTION策略或必須定義execute作爲publicprotected方法使得(在此定義一個接口時是隱式的)您期望的多態性規則適用。作爲第三種選擇,你可以調用由

type.getDeclaredMethod("execute").invoke(type.newInstance()); 
+0

正確的運行方法謝謝您拉斐爾的非常詳細的解釋,因爲機會將有它,我只是發現了兩分鐘前,我的問題的根源是由引起事實上,抽象方法是封裝私有的。 '注射'是我的解決方案。順便說一句,在Byte Buddy上做得很好! – qqilihq