Две ссылки на точные методы не равны
Следующий тест терпит неудачу
@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);
}
}
теперь скажу, что я хочу написать unit test для B:
@Test
public void test() {
A a = mock(A.class);
B b = new B(a);
b.bar();
verify(a).foo(Integer::parseInt);
}
проблема заключается в том, что тест не выполняется, потому что ссылки метода не равны.
Ответы
Ответ 1
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));
}
От Брайана Гетца; Есть ли способ сравнить lambdas?
Ответ 2
У меня нет API под рукой, но функция - это интерфейс. Integer:: parseInt, похоже, не кэшируется, поэтому он будет возвращать два разных экземпляра, которые будут сравниваться с помощью reference = > false.
Вы можете сделать это, написав Компаратор, который делает то, что вы хотите.
Ответ 3
Посмотрите на спецификацию Java Language:
15.27.4. Оценка времени лямбда-выражения.
Во время выполнения оценка выражения лямбда аналогична оценке выражения создания экземпляра класса, поскольку нормальное завершение создает ссылку на объект. Оценка лямбда-выражения отличается от выполнения лямбда-тела.
Относится и инициализируется либо новый экземпляр класса со следующими ниже свойствами, либо ссылается на существующий экземпляр класса со следующими свойствами.
...
Эти правила предназначены для обеспечения гибкости реализации Java-языка программирования, в том что:
-
При каждой оценке не нужно выделять новый объект.
-
Объекты, созданные разными лямбда-выражениями, не должны принадлежать к разным классам (если тела идентичны, например).
-
Каждый объект, созданный оценкой, не должен принадлежать одному классу (например, захваченные локальные переменные могут быть встроены).
-
Если доступен "существующий экземпляр", он не должен быть создан при предыдущей оценке лямбда (например, он мог быть выделен во время инициализации класса включения).
В принципе, это означает, что даже одно вхождение Integer::parseInt
в исходный код может приводить к разным экземплярам объектов (даже разных классов) при многократном анализе, а не о множественных появлениях. Точное решение остается за реализацией JRE. См. этот ответ, в котором обсуждается текущее поведение реализации Oracles.
Ответ 4
Хорошо, что тест не проходит. Lambdas не являются объектами, они не подпадают под свойства, такие как идентичность объекта. Вместо этого они являются adhoc реализациями функциональных интерфейсов.
Я считаю, вы не должны ожидать, что ваш код будет опираться на описанное вами поведение.