2016-01-10 4 views
8

Vorrei creare un'implementazione in fase di esecuzione per una classe astratta utilizzando Byte Buddy e sto affrontando il problema, che viene generato un java.lang.AbstractMethodError quando si richiama un metodo da un'istanza creata . Ho un abstract classe esistente come questo (che in realtà non può modificare e che in realtà contiene più logica):Byte Buddy: creazione dell'implementazione per una classe astratta

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

Utilizzando il seguente esempio minimale, vorrei che la mia Algorithm esempio per restituire un valore costante:

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()); 

Questo però porta alla seguente eccezione:

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

(quando ho sperimentalmente cambio Algorithm a un interface, tutto funziona bene, ma questo non risolve il mio problema specifico).

risposta

11

Potrebbe sorprendervi, ma la stessa cosa sarebbe accaduta se aveste generato lo stesso codice usando javac usando la stessa configurazione del caricatore di classe. Quello che osservi è implicito da come il pacchetto-privacy è specificato nel JLS. Il tuo non interfaccia classe

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

definisce un metodo package-privato. Poiché non si definisce un nome personalizzato per la classe generata, Byte Buddy genera una sottoclasse con un nome casuale che risiede nello stesso pacchetto. Byte Buddy scopre inoltre il metodo executable come sovrascrivibile dalla sottoclasse generata e lo implementa esattamente come ci si aspetterebbe.

Tuttavia, si sta utilizzando la strategia ClassLoadingStrategy.Default.WRAPPER per caricare la classe che crea un nuovo caricatore classe figlio di quello che carica Algorithm. In Java, al runtime, due pacchetti sono tuttavia uguali solo se il nome del pacchetto è uguale e entrambi i pacchetti vengono caricati dallo stesso ClassLoader. La condizione successiva non è vera per il tuo caso in modo che la JVM non applichi più il polimorfismo alla classe execute. Chiamando

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

non si sta pertanto invocando il metodo generato ma il metodo originale, astratto. Pertanto, in conformità con il JLS, viene lanciato uno AbstractMethodError.

Per risolvere questo problema, vi sia bisogno di caricare la classe generata nello stesso pacchetto, utilizzando la strategia predefinita INJECTION o si deve definire execute come public (questo è implicita quando si definisce un'interfaccia) o protected metodo tale che le regole per il polimorfismo che ti aspetti si applicano. Come terza opzione, è possibile richiamare il metodo di esecuzione corretta

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

Grazie Rafael per la vostra spiegazione molto dettagliata, guarda caso avrà, ho appena scoperto due minuti fa, che la radice del mio problema è stato causato da il fatto, che il metodo astratto era un pacchetto privato. L'INIEZIONE è la mia soluzione. Btw, ottimo lavoro su Byte Buddy! – qqilihq

Problemi correlati