按名称实例化 Java lambda 函数
Instantiate Java lambda function by name
我想在 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/471910020
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at mypackage.SimpleLambda.main(SimpleLambda.java:12)
我本以为我至少可以重新实例化静态方法引用...不,显然不是。
我一直在对 Groovy 闭包使用类似的方法并且效果很好。那么我只是对 Java 8 个 lambda 做错了什么,还是无法按名称实例化 lambda?我在网上发现了一些可以(反)序列化 lambda 的提示,所以我希望它也应该可以通过名称实例化它们。
将 lambda 实例存储在 Map 中,键入实例名称。您可以通过单例包装器 class 使地图全局可用(只需注意同步问题)。
class LambdaMap {
private HashMap<String, Consumer<String>> theMap;
private LambdaMap() {
theMap = new HashMap<>();
}
private static class INSTANCE_HOLDER {
private static LambdaMap INSTANCE = new LambdaMap();
}
public static LambdaMap getInstance() {
return INSTANCE_HOLDER.INSTANCE;
}
public Consumer<String> put(String key, Consumer<String> value) {
return theMap.put(key, value);
}
public static void Call(String aLambdaClassName, String aArg) {
Consumer<String> func = getInstance().theMap.get(aLambdaClassName);
if (func != null) {
func.accept(aArg);
}
}
}
class Host {
public static void action(String aMessage) {
System.out.println("Goodbye, " + aMessage);
}
}
public class GlobalLambdas {
public static void main(String[] args) {
LambdaMap.getInstance().put("print greeting", s -> {
System.out.println("Hello, " + s);
});
LambdaMap.getInstance().put("print goodbye", Host::action);
LambdaMap.Call("print greeting", "John");
LambdaMap.Call("print goodbye", "John");
}
}
run:
Hello, John
Goodbye, John
嗯,是Oracle的JRE/OpenJDK的一个特殊属性使用“匿名classes”,根本无法通过名字访问。但即使没有这个,也没有理由为什么它应该起作用:
Class.forName(String)
尝试通过调用者的 ClassLoader
解析 class。因此,即使 lambda 表达式是使用普通的 classes 实现的,如果通过不同的 ClassLoader
加载,也无法访问
Class.newInstance()
仅在存在 public
无参数构造函数时才有效。你不能假设有一个无参数的构造函数,也不能假设它是 public
- 整个功能的逻辑必须驻留在单个 class 中的假设是错误的。一个反例是
java.lang.reflect.Proxy
,它生成 interface
委托给 InvocationHandler
的实现。尝试通过其 class 名称重新实例化此类代理会失败,因为您需要将实际的 InvocationHandler
实例传递给代理的构造函数。原则上,JRE 特定的 lambda 表达式实现可以使用类似的模式
考虑到以上几点,您应该清楚不能说它通常与内部 classes 一起工作。为此,您必须满足很多限制条件。
关于序列化,它适用于可序列化的 lambda 表达式,因为持久形式完全脱离运行时实现 class,如 。因此生成的名称 class 不包含在序列化形式中,反序列化端可能具有完全不同的运行时实现。
我想在 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/471910020
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at mypackage.SimpleLambda.main(SimpleLambda.java:12)
我本以为我至少可以重新实例化静态方法引用...不,显然不是。
我一直在对 Groovy 闭包使用类似的方法并且效果很好。那么我只是对 Java 8 个 lambda 做错了什么,还是无法按名称实例化 lambda?我在网上发现了一些可以(反)序列化 lambda 的提示,所以我希望它也应该可以通过名称实例化它们。
将 lambda 实例存储在 Map 中,键入实例名称。您可以通过单例包装器 class 使地图全局可用(只需注意同步问题)。
class LambdaMap {
private HashMap<String, Consumer<String>> theMap;
private LambdaMap() {
theMap = new HashMap<>();
}
private static class INSTANCE_HOLDER {
private static LambdaMap INSTANCE = new LambdaMap();
}
public static LambdaMap getInstance() {
return INSTANCE_HOLDER.INSTANCE;
}
public Consumer<String> put(String key, Consumer<String> value) {
return theMap.put(key, value);
}
public static void Call(String aLambdaClassName, String aArg) {
Consumer<String> func = getInstance().theMap.get(aLambdaClassName);
if (func != null) {
func.accept(aArg);
}
}
}
class Host {
public static void action(String aMessage) {
System.out.println("Goodbye, " + aMessage);
}
}
public class GlobalLambdas {
public static void main(String[] args) {
LambdaMap.getInstance().put("print greeting", s -> {
System.out.println("Hello, " + s);
});
LambdaMap.getInstance().put("print goodbye", Host::action);
LambdaMap.Call("print greeting", "John");
LambdaMap.Call("print goodbye", "John");
}
}
run:
Hello, John
Goodbye, John
嗯,是Oracle的JRE/OpenJDK的一个特殊属性使用“匿名classes”,根本无法通过名字访问。但即使没有这个,也没有理由为什么它应该起作用:
Class.forName(String)
尝试通过调用者的ClassLoader
解析 class。因此,即使 lambda 表达式是使用普通的 classes 实现的,如果通过不同的ClassLoader
加载,也无法访问
Class.newInstance()
仅在存在public
无参数构造函数时才有效。你不能假设有一个无参数的构造函数,也不能假设它是public
- 整个功能的逻辑必须驻留在单个 class 中的假设是错误的。一个反例是
java.lang.reflect.Proxy
,它生成interface
委托给InvocationHandler
的实现。尝试通过其 class 名称重新实例化此类代理会失败,因为您需要将实际的InvocationHandler
实例传递给代理的构造函数。原则上,JRE 特定的 lambda 表达式实现可以使用类似的模式
考虑到以上几点,您应该清楚不能说它通常与内部 classes 一起工作。为此,您必须满足很多限制条件。
关于序列化,它适用于可序列化的 lambda 表达式,因为持久形式完全脱离运行时实现 class,如