I would like to create an implementation at runtime for an abstract class using Byte Buddy and I'm facing the issue, that a java.lang.AbstractMethodError
is being thrown when invoking a method from a created instance. I have an existing abstract
class like this (which I actually cannot modify and which actually contains more logic):
public abstract class Algorithm {
abstract int execute();
}
Using the following minimal sample, 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 an interface
, everything works fine, but this does not solve my specific issue).
It might surprise you but the exact same thing would happen if you had generated the same code using javac using the same class loader setup. What you observe is implied by how package-privacy is specified in the JLS. Your non-interface class
public abstract class Algorithm {
abstract int execute();
}
defines a package-private method. Since you do not define a custom name for the generated class, Byte Buddy generates a subclass with a random name that lives in the same package. Byte Buddy does further discover the executable
method as overridable from the generated subclass and implements it exactly as you would expect it to.
However, you are using the ClassLoadingStrategy.Default.WRAPPER
strategy to load the class which creates a new child-class loader of the one loading Algorithm
. In Java, at runtime, two packages are however only equal if the name of the package is equal and both packages are loaded by the same ClassLoader
. The later condition is not true for your case such that the JVM does no longer apply polymorphism to the execute
class. By calling
((Algorithm) type.newInstance()).execute();
you are therefore not invoking the generated method but the original, abstract method. Therefore - in accordance to the 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 have to define execute
as a public
(this is implicit when defining an interface) or protected
method such that the rules for polymorphism that you expect do apply. As a third option, you could invoke the correct runtime method by
type.getDeclaredMethod("execute").invoke(type.newInstance());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With