Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/338.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java8Lambdas上的反射类型推断_Java_Generics_Reflection_Lambda_Java 8 - Fatal编程技术网

Java8Lambdas上的反射类型推断

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

我在Java8中试用新的lambda,我正在寻找一种方法,在lambda类上使用反射来获得lambda函数的返回类型。我对lambda实现通用超级接口的情况特别感兴趣。在下面的代码示例中,
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());
}