Java 两个精确的方法引用不相等

Java 两个精确的方法引用不相等,java,java-8,Java,Java 8,以下测试失败 @Test public void test() { Function<String, Integer> foo = Integer::parseInt; Function<String, Integer> bar = Integer::parseInt; assertThat(foo, equalTo(bar)); } 问题是测试失败,因为方法引用不相等。我手头没有API,但函数是一个接口。Integer::parseInt似乎没

以下测试失败

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

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

我手头没有API,但函数是一个接口。Integer::parseInt似乎没有缓存,因此它将返回两个不同的实例,并通过reference=>false进行比较


您可以通过编写一个比较器使其通过,该比较器执行您想要的操作。

lambda没有缓存,这似乎是有意为之。无法比较两个lambda来确定它们是否会做相同的事情

你需要像这样做

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));
}
静态最终函数parseInt=Integer::parseInt; @试验 公开无效测试(){ 函数foo=parseInt; 功能条=parseInt; 资产(foo、equalTo(bar)); }
布赖恩·戈茨的回答

考试不及格没关系。lambda不是对象,它们不受对象标识等属性的约束。相反,它们是功能接口的临时实现


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

看看Java语言规范:

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

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

这些规则旨在为Java编程语言的实现提供灵活性,具体如下:

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

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

  • 通过求值生成的每个对象不必属于同一类(例如,捕获的局部变量可能是内联的)

  • 如果“现有实例”可用,则不需要在之前的lambda求值中创建它(例如,它可能是在封闭类的初始化过程中分配的)


原则上,这意味着即使源代码中出现一次
Integer::parseInt
,在多次求值时也可能导致不同的对象实例(即使是不同的类),更不用说多次出现。确切的决定权留给实际的JRE实现。请参阅讨论Oracle实现的当前行为。

“这阻止我为以函数为参数的方法编写单元测试。”我假设测试两个函数是否等效的方法是将内容输入到它们中,然后检查另一端是否出现相同的内容。@biziclop但您需要测试整个输入值域,有时您就是做不到:p@biziclop这不是单元测试的意义所在;它是关于测试行为的。我问测试是关于什么的原因是我认为测试定义本身不是correct@biziclop我不是在说同样的话;我的假设是,要测试的不是函数,而是该函数的用户。因此,应模拟其行为,以适应测试系统。例如,如果相等,则让mockito提供
same()
引用。但不知何故,我怀疑这是测试的真正目的。尽管如此,op并没有这么说,这仍然是一个假设。当你思考这个问题时,还有什么其他方法可以比较它们呢?我怀疑是否有任何比较器实现能够满足op的要求,至少是以一种可移植的方式。@biziclop
MethodHandle
s实际上涉及结果对象?这不是只用于方法引用创建站点吗?我希望对方法引用的求值结果是一个合成类的实例,该合成类使用直接调用该方法的代码实现请求的接口(
invokevirtual
)。@biziclop我明白你的意思,
MethodHandle
无法查询它将调用的类/方法名称。Javadoc:
[MethodHandles]不以其底层方法的名称或定义类来区分
LambdaMetafactory
确实知道
MethodHandle
的目标。看见否则,它很难生成一个调用该方法的类……为什么您认为labdas应该与方法引用相同?在@bodrin中添加的注释所有lambda都引用一个显式或隐式的方法调用,其中可能包含一些来自上下文的参数。
@Test
public void test() {
  A a = mock(A.class);
  B b = new B(a);
  b.bar();
  verify(a).foo(Integer::parseInt);
}
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));
}