Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Byte Buddy - define constructor with call to super class and initialize field

I have a class such as:

public class Sample{

private String a;
private String b;

public Sample(String a, String b)
{
    this.a = a;
    this.b = b;
}
 public String getA() {return a;}
 public String getB() {return b;}
}

I want to create a dynamic class which will inherit from Sample class, and add fields to it (String fields).

I tried to do:

DynamicType.Builder<? extends Sample> classBuilder = new ByteBuddy()
        .subclass(Sample.class, ConstructorStrategy.Default.NO_CONSTRUCTORS)
        .name("sampleSon");

classBuilder.defineConstructor(Visibility.PUBLIC)
        .withParameters(String.class, String.class, String.class)
        .intercept(MethodCall.invoke(Sample.class.getConstructor(String.class, String.class))
                .withArgument(0, 1)
                .andThen(FieldAccessor.ofField("c").setsArgumentAt(2)));

but when I tried to create a instance from this class:

Class<? extends Sample> newSampleClass= classBuilder.make().load(ClassLoader.getSystemClassLoader()).getLoaded();
Sample sample = newSampleClass.getConstructor(String.class, String.class, String.class).newInstance("a", "b", "c");

it throws an exception:

java.lang.NoSuchMethodException: sampleSon.<init>(java.lang.String, java.lang.String, java.lang.String)

What am I doing wrong? I want to create a class such that:

public class SampleSon extends Sample {
     private String c;
     public SampleSon(String a, String b, String c) {
       super(a,b);
       this.c = c;
     }

     public String getC() { return c;}
}
like image 309
Rotem ben Avatar asked Oct 18 '22 05:10

Rotem ben


1 Answers

You do not define field c before you start using it in the constructor definition.

Class<? extends Sample> clazz = new ByteBuddy()
        .subclass(Sample.class, ConstructorStrategy.Default.NO_CONSTRUCTORS)
        .name("SampleSon")
        .defineField("c", String.class, Visibility.PRIVATE)
        .defineConstructor(Visibility.PUBLIC)
        .withParameters(String.class, String.class, String.class)
        .intercept(MethodCall.invoke(Sample.class.getConstructor(String.class, String.class))
                .withArgument(0, 1)
                .andThen(FieldAccessor.ofField("c").setsArgumentAt(2)))
        .make()
        .load(ClassLoader.getSystemClassLoader())
        .getLoaded();

Note: you also need to chain all method calls according to ByteBuddy's javadoc.

like image 136
Luciano van der Veekens Avatar answered Oct 20 '22 15:10

Luciano van der Veekens