Ограничения forEach со ссылками на метод экземпляра в Java 8
предположим, что у меня есть следующий функциональный интерфейс:
public interface TemperatureObserver {
void react(BigDecimal t);
}
и затем в другом классе уже заполненный ArrayList
объектов типа TemperatureObserver
.
Предполагая, что temp
это BigDecimal
, Я могу вызвать react
в цикле, используя:
observers.forEach(item -> item.react(temp));
у меня вопрос: могу ли я использовать ссылку на метод для кода выше?
следующее не работает:
observers.forEach(TemperatureObserver::react);
сообщение об ошибке говорит мне, что
-
forEach
наArraylist observers
не применяется к типуTemperatureObserver::react
-
TemperatureObserver
не определяет способreact(TemperatureObserver)
справедливо, как forEach
ожидает в качестве аргумента Consumer<? super TemperatureObserver>
, и мой интерфейс, хотя и функциональный, не соответствует Consumer
из-за другого аргумента react
(a BigDecimal
в моем случае).
таким образом, это может быть решена, или это случай, в котором лямбда не имеет соответствующего ссылка на метод?
3 ответов
существует три вида ссылок на методы, которые можно использовать, когда одно значение доступно из потока:
-
метод без параметров потокового объекта.
class Observer { public void act() { // code here } } observers.forEach(Observer::act); observers.forEach(obs -> obs.act()); // equivalent lambda
потоковый объект становится
посмотри раздел ссылок на методы в учебнике Java. Там написано:
существует четыре вида ссылок на метод:
ссылка на статический метод:
ContainingClass::staticMethodName
ссылка на метод экземпляра конкретного объекта:
containingObject::instanceMethodName
ссылка на метод экземпляра произвольного объекта определенного типа:
ContainingType::methodName
ссылка на конструктор:
ClassName::new
там он объясняет, что т. е. TemperatureObserver::react
будет эталонным методом 3-го типа: ссылка на метод экземпляра произвольного объекта определенного типа. В контексте вашего призыва к Stream.forEach
метод, эта ссылка на метод будет эквивалентна следующему лямбда-выражению:
(TemperatureObserver item) -> item.react()
или так:
item -> item.react()
что не подходит void TemperatureObserver.react(BigDecimal t)
сигнатуру метода.
как вы уже подозреваете, есть случаи, для которых вы не можете найти эквивалентную ссылку метода для лямбды. Лямбды более гибкие, хотя ИМХО иногда они менее читабельны, чем ссылки на методы (но это вопрос вкуса, многие люди думают наоборот).
способ по-прежнему использовать ссылку на метод будет с вспомогательным методом:
public static <T, U> Consumer<? super T> consumingParam(
BiConsumer<? super T, ? super U> biConsumer,
U param) {
return t -> biConsumer.accept(t, param);
}
который вы могли бы использовать как следует:
observers.forEach(consumingParam(TemperatureObserver::react, temp));
но, честно говоря, я предпочитаю использовать лямбда.
это не работает, потому что вы перебираете обработчики, а не параметры.
например, этот код работает:
ArrayList<BigDecimal> temps = new ArrayList<>();
TemperatureObserver observer = new TemperatureObserverImpl();
temps.forEach(observer::react);