Добавление элементов в разные коллекции в одном лямбда-выражении

Возможно, я использую неправильные термины, не стесняйтесь исправлять.

У меня есть тестовый метод, который принимает 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))