10

我想在运行时使用 Byte Buddy 为抽象类创建一个实现,但我遇到了一个问题,即java.lang.AbstractMethodError从创建的实例调用方法时会抛出 a 。我有一个像这样的现有abstract类(我实际上无法修改它实际上包含更多逻辑):

public abstract class Algorithm {
    abstract int execute();
}

使用以下最小示例,我希望我的Algorithm实例返回一个常量值:

Class<?> type = new ByteBuddy()
                        .subclass(Algorithm.class)
                        .method(ElementMatchers.named("execute"))
                        .intercept(FixedValue.value(42))
                        .make()
                        .load(classLoader, ClassLoadingStrategy.Default.WRAPPER)
                        .getLoaded();
Algorithm instance = (Algorithm) type.newInstance();
System.out.println(myInstance.execute());

然而,这会导致以下异常:

Exception in thread "main" java.lang.AbstractMethodError: package.Algorithm.execute()I

(当我实验性地更改Algorithm为时interface,一切正常,但这并不能解决我的具体问题)。

4

1 回答 1

17

这可能会让您感到惊讶,但如果您使用javac使用相同的类加载器设置生成相同的代码,则会发生完全相同的事情。您观察到的内容暗示了 JLS 中如何指定包隐私。你的非接口

public abstract class Algorithm {
  abstract int execute(); 
}

定义包私有方法。由于您没有为生成的类定义自定义名称,因此 Byte Buddy 会生成一个具有随机名称的子类,该名称位于同一个包中。Byte Buddy 确实executable从生成的子类中进一步发现该方法是可覆盖的,并完全按照您的预期实现它。

但是,您正在使用该ClassLoadingStrategy.Default.WRAPPER策略来加载类,该类会创建一个 loading 的新子类加载器Algorithm。在 Java 中,在运行时,只有当包的名称相同并且两个包都由相同的 . 加载时,两个包才ClassLoader相等。后一种情况不适用于您的情况,因此 JVM 不再对execute类应用多态性。通过调用

((Algorithm) type.newInstance()).execute();

因此,您不是在调用生成的方法,而是调用原始的抽象方法。因此 - 根据 JLS - anAbstractMethodError被抛出。

要解决此问题,您需要使用默认INJECTION策略将生成的类加载到同一个包中,或者您必须定义executepublic(定义接口时这是隐式的)或protected方法,以便您期望的多态性规则执行申请。作为第三种选择,您可以通过以下方式调用正确的运行时方法

type.getDeclaredMethod("execute").invoke(type.newInstance());
于 2016-01-10T22:39:36.567 回答