好吧,我们有FunctionalInterface
:
public interface Consumer<T> {
void accept(T t);
}
我可以像以下一样使用它:
.handle(Integer p -> System.out.println(p * 2));
我们如何在代码中解析lambda参数的实际generic type
?
当我们将其用作内联实现时,从该类的方法中提取Integer
并非如此困难。
我错过了什么吗?或者只是java不支持lambda类?
更清洁:
该lambda包含MethodInvoker
(在提到的handle
中),其execute(Message<?> message)
提取实际参数以进行进一步的反射方法调用。在此之前,它使用Spring的ConversionService
将目标参数转换为目标参数。
在这种情况下,方法handle
是实际应用程序工作之前的一些配置程序。
不同的问题,但期望同一问题的解决方案:Java: get actual type of generic method with lambda parameter
答案 0 :(得分:12)
我最近添加了对解析TypeTools的lambda类型参数的支持。例如:
MapFunction<String, Integer> fn = str -> Integer.valueOf(str);
Class<?>[] typeArgs = TypeResolver.resolveRawArguments(MapFunction.class, fn.getClass());
已解析的类型args符合预期:
assert typeArgs[0] == String.class;
assert typeArgs[1] == Integer.class;
注意:底层实现使用@danielbodart概述的ConstantPool方法,该方法已知可用于Oracle JDK和OpenJDK。
答案 1 :(得分:9)
这目前可以解决,但只是以一种漂亮的hackie方式,但我先解释一下:
编写lambda时,编译器会插入一个指向LambdaMetafactory的动态调用指令和一个带有lambda主体的私有静态合成方法。常量池中的合成方法和方法句柄都包含泛型类型(如果lambda使用类型或在示例中是显式的)。
现在在运行时调用LambdaMetaFactory
并使用实现功能接口的ASM生成类,然后该方法的主体使用传递的任何参数调用私有静态方法。然后使用Unsafe.defineAnonymousClass
将其注入原始类(请参阅John Rose post),以便它可以访问私有成员等。
不幸的是,生成的类没有存储通用签名(它可以),所以你不能使用通常的反射方法来解决擦除问题
对于普通类,您可以使用Class.getResource(ClassName + ".class")
检查字节码,但对于使用Unsafe
定义的匿名类,您运气不好。但是,您可以使用JVM参数将LambdaMetaFactory
转储出来:
java -Djdk.internal.lambda.dumpProxyClasses=/some/folder
通过查看转储的类文件(使用javap -p -s -v
),可以看到它确实调用了静态方法。但问题仍然是如何从Java本身获取字节码。
不幸的是,它变成了hackie:
使用反射我们可以调用Class.getConstantPool
,然后访问MethodRefInfo以获取类型描述符。然后我们可以使用ASM来解析它并返回参数类型。把它们放在一起:
Method getConstantPool = Class.class.getDeclaredMethod("getConstantPool");
getConstantPool.setAccessible(true);
ConstantPool constantPool = (ConstantPool) getConstantPool.invoke(lambda.getClass());
String[] methodRefInfo = constantPool.getMemberRefInfoAt(constantPool.size() - 2);
int argumentIndex = 0;
String argumentType = jdk.internal.org.objectweb.asm.Type.getArgumentTypes(methodRef[2])[argumentIndex].getClassName();
Class<?> type = (Class<?>) Class.forName(argumentType);
根据Jonathan的建议更新
现在理想情况下LambdaMetaFactory
生成的类应该存储泛型类型签名(我可能会看到我是否可以向OpenJDK提交补丁)但是目前这是我们能做的最好的。上面的代码有以下问题:
答案 2 :(得分:1)
如果您的Lambdas 可序列化(SAM接口从java.io.Serializable
扩展),那么此解决方案可以为您完成:
Reflection type inference on Java 8 Lambdas
如果您自己创建了SAM接口,则可能需要添加java.io.Serializable
作为超级接口以使此方法有效。