Java 8 lambda表达式身份契约

Java 1.8的LambdaMetaFactory的JavaDoc指定lambda捕获“可能涉及分配新的函数对象,或者可能返回现有的函数对象”,但它没有指定何时以及在何种情况下它可能选择一种方式或另一种方式。

另一方面,看一下LambdaMetaFactory的实际实现,很明显当且仅当lambda表达式没有捕获任何参数时才会发生。

我想知道的是,这个行为实际上是在某个地方指定的(在JavaDoc之外)并且可以依赖吗? 很高兴知道我是否可以依赖lambda表达式的身份是否恒定。

基本上没有合同涵盖评估lambda表达式所产生的对象的身份。 这在JLS第15.27.4节“Lambda表达式的运行时评估”中有所介绍 。 本节明确地未指定创建与lambda对象重用的确切行为。 该部分的基本原理很好地解释了这一点:

这些规则旨在为Java编程语言的实现提供灵活性,其中:

  • 不需要在每次评估时分配新对象。

  • 由不同的lambda表达式生成的对象不需要属于不同的类(例如,如果主体是相同的)。

  • 评估产生的每个对象不必属于同一个类(例如,可能内联捕获的局部变量)。

  • 如果“现有实例”可用,则无需在先前的lambda评估中创建它(例如,它可能在封闭类的初始化期间分配)。

当然,您可以尝试实现,在lambda对象上调用equals()或使用== ,将它们放入IdentityHashMaps等,但由于这些确切的行为未指定,您的程序可能会改变其行为(即中断)在不同版本的JDK或Java SE的不同实现上运行时。

我在下面的评论中阅读了这个问题的交换,但我真的没有更多的东西可以提供。 也许如果你解释一下你想要做什么,我们可以提出一些建议,以替代使用lambdas作为地图中的键。

您应该将行为与身份分开。 Lambdas可用于实现行为,而单个普通类可用于通过从中创建实例来实现身份。 从您的代码派生的以下简化示例应该说明它:

 import java.util.function.Function; public class MeshBuf { { // use case 1: LayerID idCol = new LayerID<>(mbuf -> mbuf.new Col()); // use case 2: Attribute attrib=…; LayerID idVec1 = new LayerID<>(mbuf->new Vec1Layer(attrib)); // the expression new LayerID<>(…) is guaranteed to create new instances… LayerID 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 { private final Function cons; public LayerID(Function 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 { public VertexBuf.ColorArray build(Collection 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 extends Layer { public final Attribute attrib; public AttribLayer(Attribute attrib) { this.attrib = attrib; } } public class Vec1Layer extends AttribLayer { public Vec1Layer(Attribute attrib) {super(attrib);} public VertexBuf.Vec1Array build(Collection in) { FloatBuffer data = Utils.wfbuf(in.size()); for(Float d : in) data.put(d); return(new VertexBuf.Vec1Array(data, attrib)); } } }