无法通过ByteBuddy为字段分配变量。

huangapple go评论61阅读模式
英文:

Could not assign variables to fields via ByteBudy

问题

我正在学习ByteBuddy,并尝试使用以下代码。

我定义了一个Animal接口,其中包含重载的方法,并尝试创建一个类,重写这两个方法。
这部分工作正常。
然后我尝试在生成的类中添加一个字段并尝试调用该字段。
在那一点上,我遇到了异常。

以下是我的代码:

public interface Animal {

    String sound();

    String sound(boolean loud);
}

以下是来自Main::main方法的代码片段。

DynamicType.Unloaded<Animal> dogClassUnloaded = new ByteBuddy()
        .subclass(Animal.class)
        .name("Dog")

        // 我们可以使用bytebuddy修饰符
        .defineField("colour", String.class, Visibility.PUBLIC, FieldManifestation.FINAL)

        .defineConstructor(Visibility.PUBLIC)
        // 或者Java修饰符 - > Modifier.FINAL + Modifier.PRIVATE之类的
        .withParameter(String.class, "colour", Modifier.FINAL)
        .intercept(
                MethodCall.invokeSuper()
                        .andThen(
                                // 字段参数由它们的索引表示
                                FieldAccessor.ofField("colour").setsArgumentAt(0)
                        )
        )

        // 有不同的方法,字段等匹配方法
        // https://www.tabnine.com/code/java/classes/net.bytebuddy.matcher.ElementMatchers

        // 拦截无参数方法
        .method(ElementMatchers.named("sound")
                .and(ElementMatchers.takesNoArguments())
        )
        .intercept(FixedValue.value("woof quiet"))


        // 拦截带参数的重载方法
        .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);


// 如果Dog.class有其他不在Animal.class中的方法/字段会怎样
Field m = dogClassLoaded.getDeclaredField("colour");
String colour = (String) m.get(dog);
System.out.println(colour);

当我尝试执行时,我得到以下异常:

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)

它指出make()方法没有正确编译。这意味着类没有按预期构建。
我认为问题出在MethodCall.invokeSuper(),因为超类是一个接口。

解决方案是什么?
我正在使用Java-17和最新版本的bytebuddy(1.13)。

英文:

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&lt;Animal&gt; dogClassUnloaded = new ByteBuddy()
.subclass(Animal.class)
.name(&quot;Dog&quot;)
// we can use bytebuddy modifiers
.defineField(&quot;colour&quot;, String.class, Visibility.PUBLIC, FieldManifestation.FINAL)
.defineConstructor(Visibility.PUBLIC)
// or java modifiers -&gt; Modifier.FINAL + Modifier.PRIVATE like that
.withParameter(String.class, &quot;colour&quot;, Modifier.FINAL)
.intercept(
MethodCall.invokeSuper()
.andThen(
// field arguments are denoted by their index
FieldAccessor.ofField(&quot;colour&quot;).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(&quot;sound&quot;)
.and(ElementMatchers.takesNoArguments())
)
.intercept(FixedValue.value(&quot;woof quiet&quot;))
// intercepting overloaded method with arguments
.method(
ElementMatchers.named(&quot;sound&quot;)
.and(ElementMatchers.takesArgument(0, boolean.class))
)
.intercept(FixedValue.value(&quot;woof woof loud&quot;))
.make();
Class&lt;? extends Animal&gt; dogClassLoaded = dogClassUnloaded
.load(Main.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
Animal dog = dogClassLoaded.getDeclaredConstructor(String.class).newInstance(&quot;black&quot;);
// 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(&quot;colour&quot;);
String colour = (String) m.get(dog);
System.out.println(colour);

When I tried to execute I get following exception:

Exception in thread &quot;main&quot; 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)

答案1

得分: 1

你更想要做的是:

MethodCall.invoke(Object.class.getConstructor()).onSuper()

这会在父对象上调用Object构造函数,这是JVM的要求。你所做的是寻找一个具有相同签名的方法来调用super,但这种方法不存在。这相当于invokeSelf().onSuper()

英文:

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().

huangapple
  • 本文由 发表于 2023年2月19日 03:06:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/75495735.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定