В нижеприведенном фрагменте оба метода forEach()
компилируются:
public static void main(String[] args)
{
BigDecimal spent = BigDecimal.ZERO;
Stream.of(BigDecimal.ONE, BigDecimal.TEN).forEach(spent::add);
Set<BigDecimal> set = new HashSet<>();
Stream.of(BigDecimal.ONE, BigDecimal.TEN).forEach(set::add);
}
Я знаю о безумии первого примера (BigDecimal
является неизменным); проблему здесь нет:
Stream
forEach()
берет Consumer
в качестве аргумента, а подпись SAM (примечание: Single Abstract Method) говорит, что он возвращает void
;BigDecimal
.add()
возвращает BigDecimal
, а Set
.add()
(на самом деле Collection
) возвращает boolean
;Consumer
;Но это не компилируется:
// whatever is returned; BigDecimal.ZERO, null... -> compile error
Stream.of(BigDecimal.ONE).forEach(b -> { return false; });
Это по дизайну?
Помните, что тип возврата не является частью сигнатуры метода. Для вашего первого случая мы переходим к оценке времени выполнения лямбда-выражения.
Тело метода имеет эффект оценки лямбда-тела, если оно является выражением или исполнением лямбда-тела, если оно является блоком; если ожидается результат, он возвращается из метода.
Что касается второго случая
Если результат типа функции
void
, тело лямбда является выражением оператора или блоком, совместимым с void.
которого у вас нет, т.е. Тип функции Consumer#accept(Object)
void
, но ваше тело лямбда не является выражением оператора или не совместимым с void блоком. Это блок, совместимый с ценностью.
Это по дизайну?
Я сказал да. Во втором случае вы явно заявляете, что возвращаете значение. Но это не допускается методом целевого функционального интерфейса.
В вашем первом фрагменте
Stream.of(BigDecimal.ONE, BigDecimal.TEN).forEach(spent::add);
вы просто обладаете большей гибкостью. Выражение может иметь побочные эффекты, и вы можете игнорировать возвращаемое значение.
CallSite
не согласен, как, например, иMethodType
; хотя я могу понять часть «результат ожидаем», я не согласен с вышеупомянутым утверждением ...java.lang.invoke
? Что они говорят?