我正在尝试通过LambdaMetafactory动态创建BiConsumer类型的方法引用。我试图应用 https://www.cuba-platform.com/blog/think-twice-before-using-reflection/ 上的两种方法 - createVoidHandlerLambda和这里的Create BiConsumer作为字段设置器,而不反映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);
}
}
我的类是这样的:
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);
}
现在此方法不会抛出初始异常 althoug,似乎对此方法引用调用 accept 不起作用。我在此调用的日志中看不到“调用的 setValid”。仅适用于 MyClass::setValid;
请注意,您使用getMethod
和调用方。相同方法的findVirtual(…)
是多余的。如果您的起点是方法
,您可以使用unreflect
,例如。
Method method = classType.getMethod("setValid", boolean.class);
MethodHandles.Lookup caller = MethodHandles.lookup();
MethodHandle target = caller.unreflect(method);
当您动态发现方法和/或在流程中查找其他工件(如注释)时,这可能很有用。否则,只需通过find虚拟
获取方法处理
就足够了。
然后,您必须了解三种不同的功能类型:
(MyClass,布尔值)
只有正确指定所有三种类型才能告诉工厂,它必须使用代码实现方法
空接受(Object, Object)
,该代码将第一个参数转换为Myclass
,第二个参数转换为布尔
,然后将第二个参数解包为布尔
,以最终调用目标方法。
我们可以显式地指定类型,但是为了使代码尽可能可重用,我们可以在目标上调用< code>type()
,然后使用适配器方法。
erase()
将所有引用类型转换为Object
,但保留所有原始类型原样。因此将其应用于实例化方法类型会为我们提供擦除的类型。
这取决于特定的目标接口,这种简单的转换是否足够。对于java.util.function
中的接口,它是。
提高可重用性的另一点是为方法接收器类使用实际的类型参数,因为我们无论如何都将类作为参数:
public static <T>
BiConsumer<T, Boolean> createHandlerLambda(Class<T> classType) throws Throwable {
MethodHandles.Lookup caller = MethodHandles.lookup();
MethodHandle target = caller.findVirtual(classType, "setValid",
MethodType.methodType(void.class, boolean.class));
MethodType instantiated = target.type().wrap().changeReturnType(void.class);
CallSite site = LambdaMetafactory.metafactory(caller,
"accept", MethodType.methodType(BiConsumer.class),
instantiated.erase(), target, instantiated);
return (BiConsumer<T, Boolean>)site.getTarget().invoke();
}
问题内容: 我试图显式地使用LambdaMetafactory.metafactory,我不明白为什么它只能与Runnable功能接口一起使用。例如,以下代码完成了预期的工作(打印“ hello world”): 当我尝试使用其他功能接口(例如供应商)时,就会出现问题。以下代码不起作用: 这两个代码段不应该以相似的方式工作吗,这是第二个代码段中的问题? 此外,以下应等效的代码也可以正常工作: 编辑
问题内容: 我创建了一个简单的POJO: 经过一番搜索,我发现了这一点: 但是有了这个我得到了错误: 我找到了另一个解决方案: 但是这种方法不存在。 那么如何将POJO转换为JSON? 问题答案: 只需使用java Gson API : 然后您可以从json 创建一个,如下所示: 看看 GSON用户指南 ,这 SIMPLE GSON实例 的详细信息。
大家好 错误:org.json.jsonException:不是原始数组:类org.json.jsonArray 我不明白为什么这是一个问题。我想从对象中获得JSONArray。
我是一个xml和XSL的新手,使用遗留平台... 我正在寻找一个从XPath创建xml的解决方案。碰巧看到这篇文章如何从一组XPath表达式生成XML文件?帮了我大忙。 与“comments”一节中讨论的请求类似,我试图将整个XSLT作为字符串传递,并使用Saxon作为sting back接收结果。接收结果为字符串,没有问题。但是当将XSL作为字符串传递时,它会抱怨“document()”,它是
问题内容: 我们正在从Oracle JDK / JRE切换到OpenJDK。现在,我只找到了JDK,但我也想从OpenJDK获得JRE。这是为了在客户端上安装我们的应用程序而无需完整的JDK。 有没有一种方法可以从适用于Windows X64的OpenJDK创建JRE包? 问题答案: 受到文章使用jlink为非模块化应用程序构建Java运行时的启发,我使用了以下命令: 获取所有可用的openjdk
问题内容: 我正在尝试使用jQuery UI创建动态菜单。 我将从JSON文件中获取条目并创建菜单项。在大规模尝试之前,我决定做一个小演示。这是我的小提琴,它按照我想要的方式工作。现在,我无法将其与JSON文件一起使用。 工作场所 这是JSON 我如何使用JSON中的值设计我的整个菜单,其中Li类似于以下内容。 编辑: 这个问题听起来好像我还没有尝试过,但是我已经尝试过。它只是我无法理解的JSON