Byte Buddy:为抽象创建实现 class
Byte Buddy: Create implementation for an abstract class
我想使用 Byte Buddy 在运行时为抽象 class 创建一个实现,我遇到了一个问题,即从创建的对象调用方法时抛出 java.lang.AbstractMethodError
实例。我有一个像这样的现有 abstract
class(我实际上无法修改并且实际上包含更多逻辑):
public abstract class Algorithm {
abstract int execute();
}
使用以下最小样本,我希望我的 Algorithm
实例成为 return 一个常量值:
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
时,一切正常,但这并没有解决我的具体问题)。
这可能会让您感到惊讶,但如果您使用相同的 class 加载程序设置使用 javac 生成相同的代码,则会发生完全相同的事情。 JLS 中指定 package-privacy 的方式暗示了您观察到的内容。你的non-interfaceclass
public abstract class Algorithm {
abstract int execute();
}
定义了一个package-private方法。由于您没有为生成的 class 定义自定义名称,Byte Buddy 会生成一个具有随机名称的子 class,它位于同一包中。 Byte Buddy 确实进一步发现 executable
方法可从生成的子 class 中重写,并完全按照您的预期实现它。
但是,您正在使用 ClassLoadingStrategy.Default.WRAPPER
策略来加载 class,这会创建一个新的 child-class 加载程序来加载 Algorithm
。在 Java、 运行时 中,两个包 仅当包的名称相同并且两个包由相同的 [=16] 加载时才相等=]。对于您的情况,后一种情况不成立,因此 JVM 不再将多态性应用于 execute
class。通过调用
((Algorithm) type.newInstance()).execute();
因此,您调用的不是生成的方法,而是原始的抽象方法。因此 - 根据 JLS - 抛出 AbstractMethodError
。
要解决此问题,您需要在同一个包中加载生成的 class,使用默认的 INJECTION
策略,或者您必须将 execute
定义为 public
(这在定义接口时是隐含的)或 protected
方法,以便您期望的多态性规则适用。作为第三种选择,您可以通过
调用正确的运行时方法
type.getDeclaredMethod("execute").invoke(type.newInstance());
我想使用 Byte Buddy 在运行时为抽象 class 创建一个实现,我遇到了一个问题,即从创建的对象调用方法时抛出 java.lang.AbstractMethodError
实例。我有一个像这样的现有 abstract
class(我实际上无法修改并且实际上包含更多逻辑):
public abstract class Algorithm {
abstract int execute();
}
使用以下最小样本,我希望我的 Algorithm
实例成为 return 一个常量值:
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
时,一切正常,但这并没有解决我的具体问题)。
这可能会让您感到惊讶,但如果您使用相同的 class 加载程序设置使用 javac 生成相同的代码,则会发生完全相同的事情。 JLS 中指定 package-privacy 的方式暗示了您观察到的内容。你的non-interfaceclass
public abstract class Algorithm {
abstract int execute();
}
定义了一个package-private方法。由于您没有为生成的 class 定义自定义名称,Byte Buddy 会生成一个具有随机名称的子 class,它位于同一包中。 Byte Buddy 确实进一步发现 executable
方法可从生成的子 class 中重写,并完全按照您的预期实现它。
但是,您正在使用 ClassLoadingStrategy.Default.WRAPPER
策略来加载 class,这会创建一个新的 child-class 加载程序来加载 Algorithm
。在 Java、 运行时 中,两个包 仅当包的名称相同并且两个包由相同的 [=16] 加载时才相等=]。对于您的情况,后一种情况不成立,因此 JVM 不再将多态性应用于 execute
class。通过调用
((Algorithm) type.newInstance()).execute();
因此,您调用的不是生成的方法,而是原始的抽象方法。因此 - 根据 JLS - 抛出 AbstractMethodError
。
要解决此问题,您需要在同一个包中加载生成的 class,使用默认的 INJECTION
策略,或者您必须将 execute
定义为 public
(这在定义接口时是隐含的)或 protected
方法,以便您期望的多态性规则适用。作为第三种选择,您可以通过
type.getDeclaredMethod("execute").invoke(type.newInstance());