Byte Buddy: create an implementation for an abstract class

I would like to create a runtime implementation for an abstract class using Byte Buddy, and I ran into the problem that when calling a method from the created instance, java.lang.AbstractMethodError is created. I have an existing abstract class similar to this (which I actually cannot change and which actually contains more logic):

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

Using the following minimal pattern, I would like my Algorithm instance to return a constant value:

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

This, however, leads to the following exception:

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

(when I experimentally change Algorithm to interface , everything works fine, but this does not solve my specific problem).

+8
java class interface bytecode byte-buddy
source share
1 answer

This may surprise you, but the same thing will happen if you created the same code using javac using the same classloader load. What you are observing is implied in how packet confidentiality is set in JLS. Your non-interface class

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

defines a private-package method. Since you do not define a custom name for the generated class, Byte Buddy creates a subclass with a random name that lives in the same package. Byte Buddy also detects the executable method as being overridden from the generated subclass and implements it exactly as you expected.

However, you use the ClassLoadingStrategy.Default.WRAPPER strategy to load a class that creates a new loader for the child class of a single Algorithm load. In Java, at run time, two packages are only equal if the package name is equal, and both packages are loaded with the same ClassLoader . A later condition is not true for your case, so the JVM no longer applies polymorphism to the execute class. By calling

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

therefore, you are not calling the generated method, but the original, abstract method. Therefore, according to JLS, an AbstractMethodError is thrown.

To fix this problem, you either need to load the generated class in the same package using the default INJECTION strategy, or you must define execute as public (this is defined when defining the interface) or protected , so the rules for polymorphism that you expect apply. As a third option, you can call the correct execution method

 type.getDeclaredMethod("execute").invoke(type.newInstance()); 
+11
source share

All Articles