如何强制对 lamda 定义进行新的实例化
How to force a new instantiation of a lamda-definition
Java-Spec 保证给定的 lamda 定义,例如() -> "Hello World"
,compiled/converted 恰好是一个实现 class(每个定义,而不是每个 "looks" 相同的事件)。
有什么方法可以强制 java-compiler/jvm 生成一个新的 lamda 定义而不是共享一个共同的定义?我目前正在实现一个库,该库将多个函数部分编织到一个 BiFunction 中,由于 java-spec 提供的保证(EDIT:我站得更正: Java-Spec 不保证单个共享 class - 当前的参考实现虽然如此):
public <In, Out, A> BiFunction<In, Out, Out> weave(
Function<? super In, A> getter,
BiConsumer<? super Out, ? super A> consumer
) {
return (in, out) -> {
consumer.accept(out, getter.apply(in));
return out;
};
}
通过此代码生成的每个 lamda 共享相同的 lamda 定义,因此大部分是不可内联/不可优化的。
在当前的实现中,生成的 classes(甚至是非捕获 lambda 表达式的实例)的缓存是 invokedynamic
指令的 属性,它将重用第一次执行时 bootstrapping 的结果。
托管在 LambdaMetafactory
class 中的 bootstrap 方法本身将在每次调用时生成一个新的 class。因此,当您直接使用此工厂时,在当前实现下,每次调用都会得到一个新的 class。
public <In, Out, A> BiFunction<In, Out, Out> weave(
Function<? super In, A> getter,
BiConsumer<? super Out, ? super A> consumer) {
MethodHandles.Lookup l = MethodHandles.lookup();
try {
MethodHandle target = l.findStatic(l.lookupClass(), "weaveLambdaBody",
MethodType.methodType(Object.class, Function.class, BiConsumer.class,
Object.class, Object.class));
MethodType t = target.type().dropParameterTypes(0, 2);
return (BiFunction<In, Out, Out>)LambdaMetafactory.metafactory(l, "apply",
target.type().dropParameterTypes(2, 4).changeReturnType(BiFunction.class),
t, target, t) .getTarget().invokeExact(getter, consumer);
}
catch(RuntimeException | Error e) {
throw e;
}
catch(Throwable t) {
throw new IllegalStateException(t);
}
}
private static <In, Out, A> Out weaveLambdaBody(
Function<? super In, A> getter,
BiConsumer<? super Out, ? super A> consumer,
In in, Out out) {
consumer.accept(out, getter.apply(in));
return out;
}
首先,您必须将 lambda 主体脱糖到方法中。捕获的值在其参数列表中排在第一位,然后是功能接口类型的参数。 LambdaMetafactory
有关于其用法的详尽文档。
虽然出于文档目的我保留了类型参数,但应该清楚的是,通过这样的操作,您在这里失去了编译时安全性。
Java-Spec 保证给定的 lamda 定义,例如() -> "Hello World"
,compiled/converted 恰好是一个实现 class(每个定义,而不是每个 "looks" 相同的事件)。
有什么方法可以强制 java-compiler/jvm 生成一个新的 lamda 定义而不是共享一个共同的定义?我目前正在实现一个库,该库将多个函数部分编织到一个 BiFunction 中,由于 java-spec 提供的保证(EDIT:我站得更正: Java-Spec 不保证单个共享 class - 当前的参考实现虽然如此):
public <In, Out, A> BiFunction<In, Out, Out> weave(
Function<? super In, A> getter,
BiConsumer<? super Out, ? super A> consumer
) {
return (in, out) -> {
consumer.accept(out, getter.apply(in));
return out;
};
}
通过此代码生成的每个 lamda 共享相同的 lamda 定义,因此大部分是不可内联/不可优化的。
在当前的实现中,生成的 classes(甚至是非捕获 lambda 表达式的实例)的缓存是 invokedynamic
指令的 属性,它将重用第一次执行时 bootstrapping 的结果。
托管在 LambdaMetafactory
class 中的 bootstrap 方法本身将在每次调用时生成一个新的 class。因此,当您直接使用此工厂时,在当前实现下,每次调用都会得到一个新的 class。
public <In, Out, A> BiFunction<In, Out, Out> weave(
Function<? super In, A> getter,
BiConsumer<? super Out, ? super A> consumer) {
MethodHandles.Lookup l = MethodHandles.lookup();
try {
MethodHandle target = l.findStatic(l.lookupClass(), "weaveLambdaBody",
MethodType.methodType(Object.class, Function.class, BiConsumer.class,
Object.class, Object.class));
MethodType t = target.type().dropParameterTypes(0, 2);
return (BiFunction<In, Out, Out>)LambdaMetafactory.metafactory(l, "apply",
target.type().dropParameterTypes(2, 4).changeReturnType(BiFunction.class),
t, target, t) .getTarget().invokeExact(getter, consumer);
}
catch(RuntimeException | Error e) {
throw e;
}
catch(Throwable t) {
throw new IllegalStateException(t);
}
}
private static <In, Out, A> Out weaveLambdaBody(
Function<? super In, A> getter,
BiConsumer<? super Out, ? super A> consumer,
In in, Out out) {
consumer.accept(out, getter.apply(in));
return out;
}
首先,您必须将 lambda 主体脱糖到方法中。捕获的值在其参数列表中排在第一位,然后是功能接口类型的参数。 LambdaMetafactory
有关于其用法的详尽文档。
虽然出于文档目的我保留了类型参数,但应该清楚的是,通过这样的操作,您在这里失去了编译时安全性。