Java 8 lambda表达式身份契约

时间:2014-04-01 03:57:10

标签: java lambda java-8

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

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

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

2 个答案:

答案 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));
    }
  }
}