Добавление элементов в разные коллекции в одном лямбда-выражении
Возможно, я использую неправильные термины, не стесняйтесь исправлять.
У меня есть тестовый метод, который принимает Runnable
:
void expectRollback(Runnable r) { .. }
Я могу вызвать этот метод следующим образом:
expectRollback(() -> aList.add(x))
Прохладный, я понимаю лямбда! Это круто. Пусть будет супер умным...
expectRollback(() -> aList.add(x) && bList.add(y))
Но что? Это не скомпилируется: 'void' methods cannot return a value.
Не первый ли вызов также возвращает значение? В чем разница между первым и вторым вызовами?
Ответы
Ответ 1
Это тонко, но я думаю, что у меня это получилось.
В JLS 15.27.3 у нас есть:
Лямбда-выражение конгруэнтно с функциональным типом, если все верно:
- ...
- Если предполагается, что параметры лямбда имеют те же типы, что и типы параметров типа функции, тогда:
- Если результатом типа функции является
void
, тело лямбда является выражением оператора (§14.8) или блоком, совместимым с void. - ...
- ...
Теперь aList.add(x)
является выражением оператора, но aList.add(x) && bList.add(y)
не является. Вы можете видеть, что без участия лямбда - просто попробуйте использовать их в качестве утверждений:
aList.add(x); // Fine
aList.add(x) && bList.add(y); // Error: "not a statement"
Если вы переносите это выражение в вызов метода, это прекрасно, потому что вызов метода снова является выражением оператора:
rollback(() -> Boolean.valueOf(aList.add(x) && bList.add(y)));
Я на самом деле не предлагаю вам это сделать - просто пытаюсь объяснить, почему это сработает, но ваша предыдущая попытка не сделала.
Если мы предположим, что List.add
действительно вернет true
каждый раз, как задокументировано, просто используйте блок lambda:
rollback(() -> { aList.add(x); bList.add(y); });
Я бы сказал, что яснее, так как это делает более очевидным, что вы не заботитесь о значении, возвращаемом первым вызовом add
.
Ответ 2
В основном ваше первое выражение - способ вызова метода, но проигнорируйте результат:
aList.add(x) // actually returns a boolean but you ignore this result
Это не что-то новое для lambdas, так как это было так, потому что появилась java:
List<Integer> list ...
list.add(1); // returns boolean but we ignore the result almost always
Итак, почему результат не игнорируется во втором примере? Хорошо, потому что JLS
говорит так, как видно здесь. Есть 4 типа, которые будут работать, хотя:
Method Invocations
Assignments
Increment and Decrement expressions
Class Instance Creation expressions
В последнем примере используется &&
, который не является ни одним из 4 типов, описанных в JLS, таким образом, ошибка времени компиляции.
Вы можете создать метод helper
для добавления в оба списка (или как вы это сделали с Boolean.valueOf
):
public static boolean helper(int x) {
boolean result = first.add(x) && second.add(x);
return result;
}
И используйте его:
expectRollback(() -> helper(x))