The following test fails
@Test
public void test() {
Function<String, Integer> foo = Integer::parseInt;
Function<String, Integer> bar = Integer::parseInt;
assertThat(foo, equalTo(bar));
}
is there any way to make it pass?
edit: I'll try to make it more clear what I'm trying to do.
Lets say I have these classes:
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);
}
}
now lets say i want to write unit test for B:
@Test
public void test() {
A a = mock(A.class);
B b = new B(a);
b.bar();
verify(a).foo(Integer::parseInt);
}
the problem is that the test fails, because the method references are not equal.
Lambdas are not cached and this seems to be deliberate. There is no way to compare two lambdas to see if they would do the same thing.
You need to do something like
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));
}
Answer from Brian Goetz; Is there a way to compare lambdas?
I don't have the API at hand, but Function is an interface. Integer::parseInt seems not to cache, so it will return two different instances, which will be compared by reference => false.
You can make it pass by writing a Comparator, which does what you want.
Have look at the Java Language Specification:
15.27.4. Run-time Evaluation of Lambda Expressions
At run time, evaluation of a lambda expression is similar to evaluation of a class instance creation expression, insofar as normal completion produces a reference to an object. Evaluation of a lambda expression is distinct from execution of the lambda body.
Either a new instance of a class with the properties below is allocated and initialized, or an existing instance of a class with the properties below is referenced.
…
These rules are meant to offer flexibility to implementations of the Java programming language, in that:
A new object need not be allocated on every evaluation.
Objects produced by different lambda expressions need not belong to different classes (if the bodies are identical, for example).
Every object produced by evaluation need not belong to the same class (captured local variables might be inlined, for example).
If an "existing instance" is available, it need not have been created at a previous lambda evaluation (it might have been allocated during the enclosing class's initialization, for example).
In principle, this implies that even a single occurrence of Integer::parseInt
in your source code may lead to different object instances (even of different classes) when being evaluated multiple times, not to speak of multiple occurrences of it. The exact decision is left to the actual JRE implementation. See this answer discussing the current behavior of Oracle’s implementation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With