簡體   English   中英

如何使用 byte-buddy 將泛型替換為精確實現?

[英]How to replace generic with exact implementation using byte-buddy?

我正在嘗試使用 byte-buddy 將泛型替換為確切的實現。 我有以下 class:

public class ForwardingConsumer<D extends DoubleConsumer> implements DoubleConsumer {
    private final D next;

    public ForwardingConsumer(D next) {
        this.next = next;
    }

    public void accept(double value) {
        System.out.println("Some internal logic here");
        this.next.accept(value);
    }
}

以及 DoubleConsumer 的具體實現:

public class Logger implements DoubleConsumer {
    @Override
    public void accept(double value) {
        System.out.println(value);
    }
}

我想生成一個新的 class:

public class ForwardingConsumerExact<D extends Logger> implements DoubleConsumer {
    private final Logger next;

    public ForwardingConsumerExact(D next) {
        this.next = next;
    }

    public void accept(double value) {
        System.out.println("Some internal logic here");
        this.next.accept(value);
    }
}

主要目標是擁有INVOKEVIRTUAL org/example/templating/Logger.accept (D)V字節碼而不是INVOKEINTERFACE java/util/function/DoubleConsumer.accept 因此,我嘗試重新定義一個字段類型,並使用以下代碼鍵入變量:

var builder = new ByteBuddy().redefine(baseClass).name(baseClass.getName() + "Exact");
Generic genericType = Generic.Builder.rawType(exactClass).build();

builder = builder.field((ElementMatcher<FieldDescription>) fieldDescription -> true)
                .transform(new ForField((td, token) -> new Token(token.getName(), token.getModifiers(), genericType)));

builder = builder.transform(named("D"),
                                    (typeDescription, typeVariableToken) -> new TypeVariableToken("D", List.of(genericType)));

Method acceptExact = exactClass.getMethod("accept", double.class);
builder = builder.visit(MemberSubstitution.relaxed().invokable(
                    methodDescription -> methodDescription.isInvokableOn(genericType.asErasure()))
                                            .replaceWith(acceptExact)
                                            .on(ElementMatchers.any()));

但是最終的方法轉換失敗並出現以下錯誤:

Caused by: java.lang.IllegalStateException: Cannot invoke public void org.example.templating.Logger.accept(double) on parameter 0 of type interface java.util.function.DoubleConsumer
    at net.bytebuddy.asm.MemberSubstitution$Substitution$ForMethodInvocation.resolve(MemberSubstitution.java:1136)
    at net.bytebuddy.asm.MemberSubstitution$Replacement$Binding$Resolved.make(MemberSubstitution.java:1741)
    at net.bytebuddy.asm.MemberSubstitution$SubstitutingMethodVisitor.visitMethodInsn(MemberSubstitution.java:2357)

我查看了生成的字節碼(沒有最后的方法轉換),並在那里看到了一些奇怪的東西。 首先,現在有兩個字段而不是一個:

  final Ljava/util/function/DoubleConsumer; next

  // access flags 0x10
  final Lorg/example/templating/Logger; next

其次,當我閱讀這個字段時,它仍然有 DoubleConsumer 類型:

GETFIELD org/example/templating/ForwardingConsumerExact.next : Ljava/util/function/DoubleConsumer;

我的目標(更改INVOKEINTERFACE java/util/function/DoubleConsumer.accept -> INVOKEVIRTUAL org/example/templating/Logger.accept )可以通過 byte-buddy 實現嗎? 那么應該采取哪些不同的做法呢?

詳情:java 17、byte-buddy 1.12.20

JVM 實際上允許兩個名稱相同但類型不同的字段。 所以通過定義一個額外的字段,這就是你得到的。

您需要為此使用轉換,但您會遇到一個額外的問題,即此轉換應用較晚並且對大多數其他轉換不可見。 Byte Buddy 被設計為一個加法變換器,像這樣的變化並沒有得到很好的支持。

因此,如果可能的話,我建議您定義一個具有所需形狀的新 class。 除此之外,ASM 是完成此類工作的合適工具。 字節好友在其訪問API 中暴露了 ASM API。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM