Search code examples
javabytecodebyte-buddy

How do I build an concrete implementation of a Java Class from an interface using Byte-Buddy?


I have an interface, lets say it looks like this.

public interface TestObject {
    String getString();

    Long getLong();
}

I want to actually build a concrete implementation of this object using ByteBuddy.

Here's what I tried.

public class Runme {

    public static void main(String[] args) {
        ByteBuddy bb = new ByteBuddy();

        Class<?> clz = bb
                .subclass(TestObject.class)
      .method(any()).intercept(MethodDelegation.to(Interceptor.class))
            .make()
            .load(Object.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
            .getLoaded();

    try {
        Object test = clz.newInstance();

    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }

}

public class Interceptor {

    public Object intercept(@Origin String method, @AllArguments Object[] args) throws Throwable {
        System.out.println("I have intercepted a call");

        return "Hello";

    }

}

}

I get this error

Exception in thread "main" java.lang.IllegalArgumentException: None of [] allows for delegation from public boolean java.lang.Object.equals(java.lang.Object)
at net.bytebuddy.implementation.bind.MethodDelegationBinder$Processor.process(MethodDelegationBinder.java:881)
at net.bytebuddy.implementation.MethodDelegation$Appender.apply(MethodDelegation.java:1218)
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyBody(TypeWriter.java:510)
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod.apply(TypeWriter.java:444)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForCreation.create(TypeWriter.java:3193)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:1481)
at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.make(SubclassDynamicTypeBuilder.java:234)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$AbstractDelegatingBuilder.make(DynamicType.java:2177)
at com.meta.testbytebuddy.Runme.main(Runme.java:22)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

What am I doing wrong ++.. What if I want to implement multiple interfaces?

I am using ByteBuddy for code generation purposes.


Solution

  • The problem is that you are delegating to the interceptor's static methods by MethodDelegation.to(Interceptor.class) while the interceptor class only declared non-static members. You can either declare your interceptor method to be static or you delegate to an instance instead of to the class. I think the former approach would be more suitable.

    This alone will however still not work. You are returning an Object type from your interceptor while, at the same time, you are intercepting any() method. This includes the methods of your TestObject interface but also the methods declared by Object, the implicit super class. You could define your interceptor as follows to make your class compile but then Byte Buddy will cast the return type to each method's return type what will result in a ClassCastException:

    public class Interceptor {
      @RuntimeType
      public static Object intercept(@Origin String method, @AllArguments Object[] args) {
        System.out.println("I have intercepted a call");
        return "Hello";
      }
    }