两个确切的方法参考不相等

时间:2015-01-28 10:49:16

标签: java java-8

以下测试失败

@Test
public void test() {
    Function<String, Integer> foo = Integer::parseInt;
    Function<String, Integer> bar = Integer::parseInt;
    assertThat(foo, equalTo(bar));
}

有没有办法让它通过?

编辑:我会尽力让自己更清楚自己要做的事情。

让我说我有这些课程:

class A {
  public int foo(Function<String, Integer> foo) {...}
}

class B {
  private final A a; // c'tor injected
  public int bar() {
    return a.foo(Integer::parseInt);
  }
}

现在假设我想为B编写单元测试:

@Test
public void test() {
  A a = mock(A.class);
  B b = new B(a);
  b.bar();
  verify(a).foo(Integer::parseInt);
}

问题是测试失败,因为方法引用不相等。

4 个答案:

答案 0 :(得分:12)

Lambda不会被缓存,这似乎是故意的。没有办法比较两个lambdas,看看他们是否会做同样的事情。

您需要执行类似

的操作
static final Function<String, Integer> parseInt = Integer::parseInt;

@Test
public void test() {
    Function<String, Integer> foo = parseInt;
    Function<String, Integer> bar = parseInt;
    assertThat(foo, equalTo(bar));
}

Brian Goetz的回答; Is there a way to compare lambdas?

答案 1 :(得分:4)

我手头没有API,但Function是一个界面。 Integer :: parseInt似乎没有缓存,因此它将返回两个不同的实例,这些实例将通过reference =&gt;进行比较。假的。

你可以通过写一个比较器来完成它,它可以做你想要的。

答案 2 :(得分:1)

查看Java语言规范:

  

15.27.4. Run-time Evaluation of Lambda Expressions

     

在运行时,lambda表达式的计算类似于类实例创建表达式的计算,只要正常完成产生对对象的引用即可。 lambda表达式的评估不同于lambda体的执行。

     

分配并初始化具有以下属性的类的新实例,或者引用具有以下属性的类的现有实例。

     

...

     

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

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

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

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

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

  •   

原则上,这意味着即使在源代码中单次出现Integer::parseInt,在多次评估时也可能导致不同的对象实例(甚至是不同的类),而不是多次出现它。确切的决定留给实际的JRE实现。请参阅this answer,讨论Oracle实施的当前行为。

答案 3 :(得分:0)

测试没有通过是可以的。 Lambda不是对象,它们不受对象标识等属性的约束。相反,它们是功能接口的特殊实现。

我相信您不应期望您的代码依赖于您所描述的行为。