I'm very new to Byte Buddy, and I'm trying to use it to create implementations of an interface that execute getter methods on objects. My interface looks like this:
public interface Executor {
Object execute(final Object target);
}
And the idea is that if I have a class such as:
public class User {
...
public String getName() { return this.name; }
public String getSurname() { return this.surname; }
}
I need to be able to create one implementation of the Executor
interface which execute(obj)
method assumes obj
is a User
and calls its getName()
, then another implementation which does the same for getSurname()
, etc. Equivalent java code would therefore be:
public class MyHypotheticalByteBuddyExecutorImpl implements Executor {
@Override
Object execute(final Object target) {
return ((User) target).getName();
}
}
So the idea is to be able to create classes like the above for any combination of class + getter, like in this case User
+ getName()
.
I (think I) know how to make Byte Buddy create a class that almost does that:
final Method nameMethod = User.class.getMethod("getName", null);
final Class<?> myHypotheticalByteBuddyExecutorImpl =
new ByteBuddy()
.subclass(Object.class)
.implement(Executor.class)
.method(ElementMatchers.named("execute"))
.intercept(MethodCall.invoke(nameMethod).onArgument(0))
.make()
.load(ByteBuddyTest.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
...but then Byte Buddy rightly throws an Exception saying that I cannot execute method getName()
on an Object
. I'm assuming therefore that I'm lacking the ((User) target)
cast:
Exception in thread "main" java.lang.IllegalStateException: Cannot invoke public java.lang.String com.example.User.getName() on class java.lang.Object
at net.bytebuddy.implementation.MethodCall$TargetHandler$ForMethodParameter$Resolved.toStackManipulation(MethodCall.java:2527)
at net.bytebuddy.implementation.MethodCall$Appender.toStackManipulation(MethodCall.java:3541)
at net.bytebuddy.implementation.MethodCall$Appender.apply(MethodCall.java:3502)
...
I believe this could be defined as a StackManipulation
(I might be totally wrong), something like:
final StackManipulation typeCasting =
TypeCasting.to(TypeDescription.ForLoadedType.of(User.class));
But I cannot find anywhere in the Byte Buddy API how I can apply this cast (or any other code I might need for casting) to the argument of the execute(Object)
method before executing the getter.
How can I implement this?
This should work by using dynamic typing which you can configure by:
MethodCall.invoke(nameMethod)
.onArgument(0)
.withAssigner(Assigner.DEFAULT, Assigner.Typing.DYNAMIC);
The stack manipulation is used for creating custom byte code, I do not think that this is what you want to do here.