我想在 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);
}
}
但是,使用此代码(在两种变体中,使用静态方法引用并使用本地声明的 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)
我本以为我至少可以重新实例化静态方法引用...不,显然不能。
我一直在 Groovy Closures 中使用类似的方法,效果很好。那么我只是对 Java 8 lambda 做错了什么,还是无法通过名称实例化 lambda?我在网上发现了一些提示,表明 lambda 可以被序列化(反序列化),因此我希望也可以通过名称实例化它们。
嗯,使用“匿名类”是 Oracle 的 JRE/OpenJDK 的一个特殊属性,根本无法通过名称来访问它。但即使没有这个,也没有理由这样做:
-
Class.forName(String)
尝试通过调用者的方法解析该类ClassLoader
。因此,即使 lambda 表达式是使用普通类实现的,如果通过不同的类加载也无法访问ClassLoader
-
Class.newInstance()
仅当有一个时才有效public
无参数构造函数。你不能假设有一个无参构造函数,也不能假设它是public
- 整个函数的逻辑必须驻留在单个类中的假设是错误的。一个反例是
java.lang.reflect.Proxy
这会产生interface
实现委托给InvocationHandler
。尝试通过其类名重新实例化此类代理将会失败,因为您需要传递实际的代理InvocationHandler
代理构造函数的实例。原则上,JRE 特定的 lambda 表达式实现可以使用类似的模式
考虑到上述几点,应该清楚的是,您不能说它通常适用于内部类。为此,您必须满足很多限制。
关于序列化,它适用于可序列化的 lambda 表达式,因为持久化形式与运行时实现类完全分离,如这个答案中描述的 https://stackoverflow.com/a/29625025/2711488。因此,生成的类的名称不包含在序列化形式中,并且反序列化端可能具有完全不同的运行时实现。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)