I am learning ByteBuddy and tried with the following code.
I defined an Animal interface with overloaded methods and tried creating a class that overrides those both methods. That worked fine. Then I tried to have a field in the generated class and tried to call that field. I am getting exceptions at that point.
Following is my code:
public interface Animal {
String sound();
String sound(boolean loud);
}
Following is the code snippet from Main::main method.
DynamicType.Unloaded<Animal> dogClassUnloaded = new ByteBuddy()
.subclass(Animal.class)
.name("Dog")
// we can use bytebuddy modifiers
.defineField("colour", String.class, Visibility.PUBLIC, FieldManifestation.FINAL)
.defineConstructor(Visibility.PUBLIC)
// or java modifiers -> Modifier.FINAL + Modifier.PRIVATE like that
.withParameter(String.class, "colour", Modifier.FINAL)
.intercept(
MethodCall.invokeSuper()
.andThen(
// field arguments are denoted by their index
FieldAccessor.ofField("colour").setsArgumentAt(0)
)
)
// There are different ways to match the methods, fields etc.
// https://www.tabnine.com/code/java/classes/net.bytebuddy.matcher.ElementMatchers
// intercepting no argument method
.method(ElementMatchers.named("sound")
.and(ElementMatchers.takesNoArguments())
)
.intercept(FixedValue.value("woof quiet"))
// intercepting overloaded method with arguments
.method(
ElementMatchers.named("sound")
.and(ElementMatchers.takesArgument(0, boolean.class))
)
.intercept(FixedValue.value("woof woof loud"))
.make();
Class<? extends Animal> dogClassLoaded = dogClassUnloaded
.load(Main.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
Animal dog = dogClassLoaded.getDeclaredConstructor(String.class).newInstance("black");
// Animal dog = dogClassLoaded.getDeclaredConstructor().newInstance();
var x = dog.sound();
var y = dog.sound(true);
System.out.println(x);
System.out.println(y);
// What if Dog.class has other methods / fields not in Animal.class
Field m = dogClassLoaded.getDeclaredField("colour");
String colour = (String) m.get(dog);
System.out.println(colour);
When I tried to execute I get following exception:
Exception in thread "main" java.lang.IllegalStateException: public Dog(java.lang.String) does not accept 0 arguments
at net.bytebuddy.implementation.MethodCall$Appender.toStackManipulation(MethodCall.java:3553)
at net.bytebuddy.implementation.MethodCall$Appender.apply(MethodCall.java:3522)
at net.bytebuddy.implementation.bytecode.ByteCodeAppender$Compound.apply(ByteCodeAppender.java:156)
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyCode(TypeWriter.java:730)
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyBody(TypeWriter.java:715)
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod.apply(TypeWriter.java:622)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForCreation.create(TypeWriter.java:6043)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:2224)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$UsingTypeWriter.make(DynamicType.java:4050)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase.make(DynamicType.java:3734)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Delegator.make(DynamicType.java:3986)
at tutorial.example_1.Main.main(Main.java:55)
It points that the make() method is not compiling properly. That means the class is not building as intended.
I think MethodCall.invokeSuper()
is where the issue is since super class is an interface.
What is the solution? I am using Java-17 and latest version of bytebuddy (1.13)
What you rather want to do is to:
MethodCall.invoke(Object.class.getConstructor()).onSuper()
That invokes the Object
constructor on the super object what is the JVM requirement. What you are doing is to look for a method of equal signature to invoke super on which does not exist. It is a synonym for invokeSelf().onSuper()
.