2016-01-10 32 views
8

Chciałbym utworzyć implementację w środowisku wykonawczym dla klasy abstrakcyjnej za pomocą Bajt Buddy i mam do czynienia z problemem, który jest zgłaszany podczas wywoływania metody z utworzonej instancji . Mam istniejący abstract klasę tak (który tak naprawdę nie można modyfikować i który rzeczywiście zawiera więcej logiki):Bajt Buddy: Utwórz implementację dla abstrakcyjnej klasy

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

stosując następującą minimalną próbkę, chciałbym moje wystąpienie Algorithm powrót stałą wartość:

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

jednak ten prowadzi do następującym wyjątkiem:

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

(kiedy eksperymentalnie zmienić Algorithm do interface, wszystko działa dobrze, ale to nie rozwiązuje mojego konkretnego problemu).

Odpowiedz

11

Może Cię zaskoczyć, ale to samo by się stało, gdybyś wygenerował ten sam kod przy użyciu javac przy użyciu tej samej konfiguracji modułu ładującego klasy. To, co obserwujesz, wynika z tego, w jaki sposób określona jest prywatność pakietów w JLS. Twój non-interface klasa

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

definiuje metodę pakiet-prywatnego. Ponieważ nie definiujesz niestandardowej nazwy wygenerowanej klasy, Bajt Buddy generuje podklas z losową nazwą, która znajduje się w tym samym pakiecie. Bajt Buddy odkrywa ponadto, że metoda executable jest nadpisywalna z wygenerowanej podklasy i implementuje ją dokładnie tak, jak byś tego oczekiwał.

Jednak używasz strategii ClassLoadingStrategy.Default.WRAPPER do załadowania klasy, która tworzy nowy program ładujący dla klasy podrzędnej dla jednej ładującej Algorithm. W języku Java: w czasie wykonywania, dwa pakiety są jednak równe tylko wtedy, gdy nazwa pakietu jest równa i oba pakiety są ładowane przez ten sam ClassLoader. Późniejszy warunek nie jest prawdą dla twojego przypadku, tak, że JVM nie stosuje już polimorfizmu do klasy execute. Nie wywołując wygenerowanej metody, ale wywołując oryginalną, abstrakcyjną metodę, wywołując nieautoryzowane wywołanie. Dlatego - zgodnie z JLS - jest wyrzucany AbstractMethodError.

Aby rozwiązać ten problem, należy albo załadować wygenerowanej klasy w tym samym opakowaniu, stosując strategię domyślny INJECTION czy trzeba określić execute jako public (jest niejawny podczas definiowania interfejs) lub protected sposób taki, że obowiązują zasady dotyczące polimorfizmu, których się spodziewasz. Jako trzecią opcję możesz wywołać prawidłową metodę wykonywania przez:

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

Dziękuję Ci Rafael za bardzo szczegółowe wyjaśnienie, ponieważ szansa będzie to mieć, właśnie odkryłem dwie minuty temu, że przyczyną mojego problemu był fakt, że abstrakcyjną metodą był pakiet prywatny. "WTRYSK" to moje rozwiązanie. Btw, świetna robota na Byte Buddy! – qqilihq