JavaDoc for the LambdaMetaFactory
of Java 1.8指定lambda捕获“可能涉及分配新的函数对象,或者可能返回现有的函数对象”,但它没有指定何时以及在什么情况下它可能选择一种方式或者其他
另一方面,查看LambdaMetaFactory
的实际实现,很明显当且仅当lambda表达式没有捕获任何参数时才会发生。
我想知道的是,这个行为是否实际指定在某个地方(在JavaDoc之外)并且可以依赖?很高兴知道我是否可以依赖lambda表达式的身份是否恒定。
答案 0 :(得分:6)
基本上没有合同涵盖评估lambda表达式所产生的对象的身份。这在JLS第15.27.4节Run-time Evaluation of Lambda Expressions中有所介绍。本节明确地未指定创建与lambda对象重用的确切行为。该部分的基本原理很好地解释了这一点:
这些规则旨在为Java编程语言的实现提供灵活性,其中包括:
不需要在每次评估时分配新对象。
由不同的lambda表达式生成的对象不必属于不同的类(例如,如果主体是相同的)。
评估产生的每个对象都不必属于同一个类(例如,可能内联捕获的局部变量)。
如果"现有实例"它是可用的,它不需要在之前的lambda评估中创建(例如,它可能是在封闭类的初始化期间分配的。)
当然,您可以尝试实现,在lambda对象上调用equals()
或使用==
,将它们放入IdentityHashMaps
等,但由于这些确切的行为未指定,当您运行不同版本的JDK或Java SE的不同实现时,您的程序可能会改变其行为(即中断)。
我在下面的评论中读到了这个问题,但我还没有提供更多的东西。也许如果你解释一下你正在尝试做什么,我们可以提出一些建议来替代使用lambdas作为地图中的键。
答案 1 :(得分:2)
您应该将行为与身份分开。 Lambdas可用于实现行为,而单个普通类可用于通过从中创建实例来实现身份。从您的代码派生的以下简化示例应该说明它:
import java.util.function.Function;
public class MeshBuf
{
{
// use case 1:
LayerID<Col> idCol = new LayerID<>(mbuf -> mbuf.new Col());
// use case 2:
Attribute attrib=…;
LayerID<Vec1Layer> idVec1 = new LayerID<>(mbuf->new Vec1Layer(attrib));
// the expression new LayerID<>(…) is guaranteed to create new instances…
LayerID<Vec1Layer> idVec2 = new LayerID<>(mbuf->new Vec1Layer(attrib));
// therefore idVec1 != idVec2 even if referring to the same expression
}
// single class is enough for maintaining the identity
public static final class LayerID<L> {
private final Function<MeshBuf, L> cons;
public LayerID(Function<MeshBuf, L> f) {
cons = f;
}
public L cons(MeshBuf buf) {
return cons.apply(buf);
}
}
// the other classes are taken from your code, unchanged
public class Col extends Layer<Color> {
public VertexBuf.ColorArray build(Collection<Color> in) {
FloatBuffer data = Utils.wfbuf(in.size() * 4);
for(Color c : in) {
data.put(c.getRed() / 255.0f); data.put(c.getGreen() / 255.0f);
data.put(c.getBlue() / 255.0f); data.put(c.getAlpha() / 255.0f);
}
return(new VertexBuf.ColorArray(data));
}
}
public abstract class AttribLayer<T> extends Layer<T> {
public final Attribute attrib;
public AttribLayer(Attribute attrib) {
this.attrib = attrib;
}
}
public class Vec1Layer extends AttribLayer<Float> {
public Vec1Layer(Attribute attrib) {super(attrib);}
public VertexBuf.Vec1Array build(Collection<Float> in) {
FloatBuffer data = Utils.wfbuf(in.size());
for(Float d : in)
data.put(d);
return(new VertexBuf.Vec1Array(data, attrib));
}
}
}