Java8Lambdas上的反射类型推断
我在Java8中试用新的lambda,我正在寻找一种方法,在lambda类上使用反射来获得lambda函数的返回类型。我对lambda实现通用超级接口的情况特别感兴趣。在下面的代码示例中,Java8Lambdas上的反射类型推断,java,generics,reflection,lambda,java-8,Java,Generics,Reflection,Lambda,Java 8,我在Java8中试用新的lambda,我正在寻找一种方法,在lambda类上使用反射来获得lambda函数的返回类型。我对lambda实现通用超级接口的情况特别感兴趣。在下面的代码示例中,MapFunction是泛型超级接口,我正在寻找一种方法来找出什么类型绑定到泛型参数T 虽然Java在编译器之后丢弃了很多泛型类型信息,但泛型超类和泛型超接口的子类(和匿名子类)确实保留了这些类型信息。通过反射,这些类型是可访问的。在下面的示例(案例1)中,反射告诉my,MapFunction的MyMapper
MapFunction
是泛型超级接口,我正在寻找一种方法来找出什么类型绑定到泛型参数T
虽然Java在编译器之后丢弃了很多泛型类型信息,但泛型超类和泛型超接口的子类(和匿名子类)确实保留了这些类型信息。通过反射,这些类型是可访问的。在下面的示例(案例1)中,反射告诉my,MapFunction
的MyMapper
实现将java.lang.Integer
绑定到泛型类型参数T
即使对于本身是泛型的子类,如果已知其他一些泛型参数,也有一些方法可以找到绑定到泛型参数的内容。在下面的例子中考虑案例2,<代码>标识映射器< /代码>,其中<代码> f>代码>和<代码> t>代码>绑定到同一类型。当我们知道时,如果我们知道参数类型T
(在我的例子中我们知道),我们就知道类型F
现在的问题是,如何为Java8Lambdas实现类似的功能?因为它们实际上不是泛型超级接口的常规子类,所以上述方法不起作用。
具体来说,parseLambda
将java.lang.Integer
绑定到T
,而identityLambda
将其绑定到F
和T
PS:理论上,应该可以对lambda代码进行反编译,然后使用嵌入式编译器(如JDT)并利用其类型推断。我希望有一个更简单的方法来做到这一点;-)
/**
*超级界面。
*/
公共接口映射函数{
T图(F值);
}
/**
*案例1:非泛型子类。
*/
公共类MyMapper实现映射函数{
公共整数映射(字符串值){
返回整数.valueOf(value);
}
}
/**
*泛型子类
*/
公共类IdentityMapper实现映射函数{
公共电子地图(E值){
返回值;
}
}
/**
*通过lambda进行实例化
*/
公共映射函数parseLambda=(字符串str)->{return Integer.valueOf(str);}
公共映射函数identityLambda=(值)->{return value;}
公共静态void main(字符串[]args)
{
//案例1
getReturnType(MyMapper.class);//->返回java.lang.Integer
//案例2
getReturnTypeRelativeToParameter(IdentityMapper.class,String.class);//->返回java.lang.String
}
私有静态类getReturnType(类实现类)
{
类型superType=implementingClass.GetGenericInterface()[0];
if(参数化类型的超类型实例){
ParameteredType ParameteredType=(ParameteredType)超类型;
返回(类)ParameteredType.getActualTypeArguments()[1];
}
否则返回null;
}
私有静态类getReturnTypeRelativeToParameter(类实现类,类参数类型)
{
类型superType=implementingClass.GetGenericInterface()[0];
if(参数化类型的超类型实例){
ParameteredType ParameteredType=(ParameteredType)超类型;
TypeVariable inputType=(TypeVariable)ParameteredType.getActualTypeArguments()[0];
TypeVariable returnType=(TypeVariable)ParameteredType.getActualTypeArguments()[1];
if(inputType.getName().equals(returnType.getName())){
返回参数类型;
}
否则{
//确定组合返回类型的一些逻辑
}
}
返回null;
}
参数化类型信息仅在运行时对绑定的代码元素可用,也就是说,专门编译成类型。Lambda也做同样的事情,但是由于Lambda被分解为方法而不是类型,因此没有类型来捕获该信息
考虑以下几点:
import java.util.Arrays;
import java.util.function.Function;
public class Erasure {
static class RetainedFunction implements Function<Integer,String> {
public String apply(Integer t) {
return String.valueOf(t);
}
}
public static void main(String[] args) throws Exception {
Function<Integer,String> f0 = new RetainedFunction();
Function<Integer,String> f1 = new Function<Integer,String>() {
public String apply(Integer t) {
return String.valueOf(t);
}
};
Function<Integer,String> f2 = String::valueOf;
Function<Integer,String> f3 = i -> String.valueOf(i);
for (Function<Integer,String> f : Arrays.asList(f0, f1, f2, f3)) {
try {
System.out.println(f.getClass().getMethod("apply", Integer.class).toString());
} catch (NoSuchMethodException e) {
System.out.println(f.getClass().getMethod("apply", Object.class).toString());
}
System.out.println(Arrays.toString(f.getClass().getGenericInterfaces()));
}
}
}
导入java.util.array;
导入java.util.function.function;
公共类擦除{
静态类RetainedFunction实现函数{
公共字符串应用(整数t){
返回字符串.valueOf(t);
}
}
公共静态void main(字符串[]args)引发异常{
函数f0=新的保留函数();
函数f1=新函数(){
公共字符串应用(整数t){
返回字符串.valueOf(t);
}
};
函数f2=字符串::valueOf;
函数f3=i->String.valueOf(i);
for(函数f:Arrays.asList(f0、f1、f2、f3)){
试一试{
System.out.println(f.getClass().getMethod(“apply”,Integer.class.toString());
}捕获(无此方法例外){
System.out.println(f.getClass().getMethod(“apply”,Object.class).toString());
}
System.out.println(Arrays.toString(f.getClass().getGenericInterfaces());
}
}
}
f0
和f1
都保留了它们的泛型类型信息,正如您所期望的那样。但是,由于它们是未绑定的方法,已被擦除到函数中,f2
和f3
没有。如何将lambda代码映射到接口实现的确切决定权留给实际的运行时环境。原则上,实现同一原始接口的所有lambda都可以共享一个运行时类,就像它一样。为特定的lambda使用不同的类是由实际的LambdaMetafactory
实现执行的优化,而不是旨在帮助调试或反射的功能
因此,即使您在lambda接口实现的实际运行时类中找到了更详细的信息
java -Djdk.internal.lambda.dumpProxyClasses=/some/folder
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);
// sample how to use
public static interface SomeFunction<I, O> extends java.io.Serializable {
List<O> applyTheFunction(Set<I> value);
}
public static void main(String[] args) throws Exception {
SomeFunction<Double, Long> lambda = (set) -> Collections.singletonList(set.iterator().next().longValue());
SerializedLambda sl = getSerializedLambda(lambda);
Method m = getLambdaMethod(sl);
System.out.println(m);
System.out.println(m.getGenericReturnType());
for (Type t : m.getGenericParameterTypes()) {
System.out.println(t);
}
// prints the following
// (the method) private static java.util.List test.ClassWithLambdas.lambda$0(java.util.Set)
// (the return type, including *Long* as the generic list type) java.util.List<java.lang.Long>
// (the parameter, including *Double* as the generic set type) java.util.Set<java.lang.Double>
// getting the SerializedLambda
public static SerializedLambda getSerializedLambda(Object function) {
if (function == null || !(function instanceof java.io.Serializable)) {
throw new IllegalArgumentException();
}
for (Class<?> clazz = function.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
try {
Method replaceMethod = clazz.getDeclaredMethod("writeReplace");
replaceMethod.setAccessible(true);
Object serializedForm = replaceMethod.invoke(function);
if (serializedForm instanceof SerializedLambda) {
return (SerializedLambda) serializedForm;
}
}
catch (NoSuchMethodError e) {
// fall through the loop and try the next class
}
catch (Throwable t) {
throw new RuntimeException("Error while extracting serialized lambda", t);
}
}
throw new Exception("writeReplace method not found");
}
// getting the synthetic static lambda method
public static Method getLambdaMethod(SerializedLambda lambda) throws Exception {
String implClassName = lambda.getImplClass().replace('/', '.');
Class<?> implClass = Class.forName(implClassName);
String lambdaName = lambda.getImplMethodName();
for (Method m : implClass.getDeclaredMethods()) {
if (m.getName().equals(lambdaName)) {
return m;
}
}
throw new Exception("Lambda Method not found");
}
MapFunction<String, Integer> fn = str -> Integer.valueOf(str);
Class<?>[] typeArgs = TypeResolver.resolveRawArguments(MapFunction.class, fn.getClass());
assert typeArgs[0] == String.class;
assert typeArgs[1] == Integer.class;
public void call(Callable<?> c) {
// Assumes c is a lambda
Class<?> callableType = TypeResolver.resolveRawArguments(Callable.class, c.getClass());
}