【发布时间】:2020-05-04 22:46:33
【问题描述】:
我正在尝试通过 LambdaMetafactory 动态创建 BiConsumer 类型的方法引用。 我试图应用在https://www.cuba-platform.com/blog/think-twice-before-using-reflection/ 上找到的两种方法 - createVoidHandlerLambda 和这里Create BiConsumer as Field setter without reflection Holger 的答案。
但是在这两种情况下我都有以下错误:
Exception in thread "main" java.lang.AbstractMethodError: Receiver class org.home.ref.App$$Lambda$15/0x0000000800066040 does not define or inherit an implementation of the resolved method abstract accept(Ljava/lang/Object;Ljava/lang/Object;)V of interface java.util.function.BiConsumer.
at org.home.ref.App.main(App.java:20)
我的代码是这样的:
public class App {
public static void main(String[] args) throws Throwable {
MyClass myClass = new MyClass();
BiConsumer<MyClass, Boolean> setValid = MyClass::setValid;
setValid.accept(myClass, true);
BiConsumer<MyClass, Boolean> mappingMethodReferences = createHandlerLambda(MyClass.class);
mappingMethodReferences.accept(myClass, true);
}
@SuppressWarnings("unchecked")
public static BiConsumer<MyClass, Boolean> createHandlerLambda(Class<?> classType) throws Throwable {
Method method = classType.getMethod("setValid", boolean.class);
MethodHandles.Lookup caller = MethodHandles.lookup();
CallSite site = LambdaMetafactory.metafactory(caller,
"accept",
MethodType.methodType(BiConsumer.class),
MethodType.methodType(void.class, MyClass.class, boolean.class),
caller.findVirtual(classType, method.getName(),
MethodType.methodType(void.class, method.getParameterTypes()[0])),
MethodType.methodType(void.class, classType, method.getParameterTypes()[0]));
MethodHandle factory = site.getTarget();
return (BiConsumer<MyClass, Boolean>) factory.invoke();
}
public static <C, V> BiConsumer<C, V> createSetter(Class<?> classType) throws Throwable {
Field field = classType.getDeclaredField("valid");
MethodHandles.Lookup lookup = MethodHandles.lookup();
final MethodHandle setter = lookup.unreflectSetter(field);
final CallSite site = LambdaMetafactory.metafactory(lookup,
"accept", MethodType.methodType(BiConsumer.class, MethodHandle.class),
setter.type().erase(), MethodHandles.exactInvoker(setter.type()), setter.type());
return (BiConsumer<C, V>)site.getTarget().invokeExact(setter);
}
}
MyClass 如下所示:
public class MyClass {
public boolean valid;
public void setValid(boolean valid) {
this.valid = valid;
System.out.println("Called setValid");
}
}
我将不胜感激。
编辑#1。 在咨询@Holger 之后,我将 createSetter 方法修改为:
@SuppressWarnings("unchecked")
public static <C, V> BiConsumer<C, V> createSetter(Class<?> classType) throws Throwable {
Field field = classType.getDeclaredField("valid");
MethodHandles.Lookup lookup = MethodHandles.lookup();
final MethodHandle setter = lookup.unreflectSetter(field);
MethodType type = setter.type();
if(field.getType().isPrimitive())
type = type.wrap().changeReturnType(void.class);
final CallSite site = LambdaMetafactory.metafactory(lookup,
"accept", MethodType.methodType(BiConsumer.class, MethodHandle.class),
type.erase(), MethodHandles.exactInvoker(setter.type()), type);
return (BiConsumer<C, V>)site.getTarget().invokeExact(setter);
}
现在这个方法不会抛出初始异常,尽管在这个方法引用上调用 accept 似乎没有效果。我在此调用的日志中没有看到“Called setValid”。仅适用于 MyClass::setValid;
【问题讨论】:
-
您从this Q&A 复制的代码专门用于直接访问该字段,即无需调用setter。
-
我知道,Setter 是用于 createHandlerLambda 方法的。基本上我找到了两种方法来解决这个问题(一种来自你的问题),但我不能让它们工作;/对于你的方法,我使用直接访问布尔字段有效。这就是它公开的原因。
-
@Holger,我现在明白了。是的,您的解决方案有效,谢谢。值更改不是通过设置器进行的。这就是为什么我没有看到该日志行。我现在想知道如何使其他解决方案发挥作用。
标签: java reflection method-reference lambda-metafactory