按名称实例化Java lambda函数

rec*_*rec 9 java lambda classloader java-8

我想在Java 8中创建一个lambda函数,获取它的类名,然后再从它的类名实例化该函数.

这是我尝试的:

import java.util.function.Consumer;

public class SimpleLambda
{
    public static void call(String aLambdaClassName, String aArg) throws Exception
    {
        Class<Consumer<String>> lClass = (Class<Consumer<String>>) Class.forName(aLambdaClassName);
        Consumer<String> newlamba = lClass.newInstance();
        newlamba.accept(aArg);
    }

    public static void main(String[] args) throws Exception
    {
        {
            // Attempt with a static method as lambda
            Consumer<String> lambda = Host::action;
            String classname = lambda.getClass().getName();
            call(classname, "Hello world");
        }

        {
            // Attempt with a locally defined lambda
            Consumer<String> lambda = (s) -> { System.out.println(s); };
            String classname = lambda.getClass().getName();
            call(classname, "Hello world");
        }
    }
}

class Host {
    public static void action(String aMessage) {
        System.out.println(aMessage);
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,使用此代码(在两种变体中,使用静态方法引用并使用本地声明的lambda),我得到一个异常:

Exception in thread "main" java.lang.ClassNotFoundException: mypackage.SimpleLambda$$Lambda$1/471910020
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:264)
    at mypackage.SimpleLambda.main(SimpleLambda.java:12)
Run Code Online (Sandbox Code Playgroud)

我本以为我至少可以重新实例化静态方法引用... nope,显然不是.

我一直在使用与Groovy Closures类似的方法,并且运行良好.所以我只是对Java 8 lambdas做错了,还是不可能通过名称实例化lambdas?我在网上发现了lambdas可以(de)序列化的一些提示,所以我希望它也可以通过名称实例化它们.

Hol*_*ger 5

好吧,Oracle的JRE/OpenJDK的一个特殊属性是使用"匿名类",根本不能通过名称访问它.但即使没有这个,也没有理由认为这应该有效:

  • Class.forName(String)尝试通过调用者解析类ClassLoader.因此,即使使用普通类实现lambda表达式,如果通过不同的方式加载也无法访问ClassLoader
  • Class.newInstance()仅在存在public无参数构造函数时才有效.你不能假设有一个no-arg构造函数,也不是public
  • 假设整个函数的逻辑必须驻留在单个类中是错误的.一个反例可以java.lang.reflect.Proxy生成interface委托给一个的实现InvocationHandler.尝试通过其类名重新实例化此类代理将失败,因为您需要将实际InvocationHandler实例传递给代理的构造函数.原则上,JRE特定的lambda表达式实现可以使用类似的模式

考虑到上述几点,应该清楚的是,你不能说它一般适用于内部类.你必须要满足很多限制.


关于序列化,它适用于可序列化的lambda表达式,因为持久化表单与运行时实现类完全分离,如本答案中所述.因此,生成的类的名称不包含在序列化形式中,反序列化结束可能具有完全不同的运行时实现.