Java 8 中带有实例方法引用的 forEach 的局限性
Limitations of forEach with instance method references in Java 8
假设我有以下功能接口:
public interface TemperatureObserver {
void react(BigDecimal t);
}
然后在另一个 class 中已经填充了 ArrayList
类型 TemperatureObserver
的对象。
假设 temp
是 BigDecimal
,我可以使用以下循环调用 react
:
observers.forEach(item -> item.react(temp));
我的问题:我可以为上面的代码使用方法参考吗?
以下无效:
observers.forEach(TemperatureObserver::react);
错误消息告诉我
forEach
中的Arraylist observers
不适用于TemperatureObserver::react
类型
TemperatureObserver
没有定义方法react(TemperatureObserver)
很公平,因为 forEach
期望参数 a Consumer<? super TemperatureObserver>
,而我的界面虽然功能正常,但不符合 Consumer
因为 [=18 的不同参数=](在我的例子中是 BigDecimal
)。
请问这个能解决吗,还是lambda没有对应的方法引用的情况?
它不起作用,因为您迭代处理程序,而不是参数。
例如,这段代码有效:
ArrayList<BigDecimal> temps = new ArrayList<>();
TemperatureObserver observer = new TemperatureObserverImpl();
temps.forEach(observer::react);
当流中的单个值可用时,可以使用三种方法引用:
流对象的无参数方法。
class Observer {
public void act() {
// code here
}
}
observers.forEach(Observer::act);
observers.forEach(obs -> obs.act()); // equivalent lambda
流式对象成为方法的 this
对象。
以流式对象作为参数的静态方法。
class Other {
public static void act(Observer o) {
// code here
}
}
observers.forEach(Other::act);
observers.forEach(obs -> Other.act(obs)); // equivalent lambda
以流对象作为参数的非静态方法。
class Other {
void act(Observer o);
}
Other other = new Other();
observers.forEach(other::act);
observers.forEach(obs -> other.act(obs)); // equivalent lambda
还有一个构造函数引用,但这与这个问题并不相关。
因为你有一个外部值temp
,并且你想使用方法引用,你可以做第三个选项:
class Temp {
private final BigDecimal temp;
public Temp(BigDecimal temp) {
this.temp = temp;
}
public void apply(TemperatureObserver observer) {
observer.react(this.temp);
}
}
Temp tempObj = new Temp(temp);
observers.forEach(tempObj::apply);
看看Method References section in the Java Tutorial。上面写着:
There are four kinds of method references:
Reference to a static method: ContainingClass::staticMethodName
Reference to an instance method of a particular object: containingObject::instanceMethodName
Reference to an instance method of an arbitrary object of a particular type: ContainingType::methodName
Reference to a constructor: ClassName::new
那里解释说,即 TemperatureObserver::react
将是第三种类型的方法引用:对特定类型的任意对象的实例方法的引用。在您调用 Stream.forEach
方法的上下文中,该方法引用将等效于以下 lambda 表达式:
(TemperatureObserver item) -> item.react()
或者只是:
item -> item.react()
这与您的 void TemperatureObserver.react(BigDecimal t)
方法签名不匹配。
正如您已经怀疑的那样,在某些情况下您找不到 lambda 的等效方法参考。 Lambda 更灵活,尽管恕我直言,有时它们的可读性不如方法引用(但这是一个品味问题,许多人认为相反)。
一种仍然使用方法引用的方法是使用辅助方法:
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));
但是,老实说,我更喜欢使用 lambda。
假设我有以下功能接口:
public interface TemperatureObserver {
void react(BigDecimal t);
}
然后在另一个 class 中已经填充了 ArrayList
类型 TemperatureObserver
的对象。
假设 temp
是 BigDecimal
,我可以使用以下循环调用 react
:
observers.forEach(item -> item.react(temp));
我的问题:我可以为上面的代码使用方法参考吗?
以下无效:
observers.forEach(TemperatureObserver::react);
错误消息告诉我
forEach
中的Arraylist observers
不适用于TemperatureObserver::react
类型
TemperatureObserver
没有定义方法react(TemperatureObserver)
很公平,因为 forEach
期望参数 a Consumer<? super TemperatureObserver>
,而我的界面虽然功能正常,但不符合 Consumer
因为 [=18 的不同参数=](在我的例子中是 BigDecimal
)。
请问这个能解决吗,还是lambda没有对应的方法引用的情况?
它不起作用,因为您迭代处理程序,而不是参数。
例如,这段代码有效:
ArrayList<BigDecimal> temps = new ArrayList<>();
TemperatureObserver observer = new TemperatureObserverImpl();
temps.forEach(observer::react);
当流中的单个值可用时,可以使用三种方法引用:
流对象的无参数方法。
class Observer { public void act() { // code here } } observers.forEach(Observer::act); observers.forEach(obs -> obs.act()); // equivalent lambda
流式对象成为方法的
this
对象。以流式对象作为参数的静态方法。
class Other { public static void act(Observer o) { // code here } } observers.forEach(Other::act); observers.forEach(obs -> Other.act(obs)); // equivalent lambda
以流对象作为参数的非静态方法。
class Other { void act(Observer o); } Other other = new Other(); observers.forEach(other::act); observers.forEach(obs -> other.act(obs)); // equivalent lambda
还有一个构造函数引用,但这与这个问题并不相关。
因为你有一个外部值temp
,并且你想使用方法引用,你可以做第三个选项:
class Temp {
private final BigDecimal temp;
public Temp(BigDecimal temp) {
this.temp = temp;
}
public void apply(TemperatureObserver observer) {
observer.react(this.temp);
}
}
Temp tempObj = new Temp(temp);
observers.forEach(tempObj::apply);
看看Method References section in the Java Tutorial。上面写着:
There are four kinds of method references:
Reference to a static method:
ContainingClass::staticMethodName
Reference to an instance method of a particular object:
containingObject::instanceMethodName
Reference to an instance method of an arbitrary object of a particular type:
ContainingType::methodName
Reference to a constructor:
ClassName::new
那里解释说,即 TemperatureObserver::react
将是第三种类型的方法引用:对特定类型的任意对象的实例方法的引用。在您调用 Stream.forEach
方法的上下文中,该方法引用将等效于以下 lambda 表达式:
(TemperatureObserver item) -> item.react()
或者只是:
item -> item.react()
这与您的 void TemperatureObserver.react(BigDecimal t)
方法签名不匹配。
正如您已经怀疑的那样,在某些情况下您找不到 lambda 的等效方法参考。 Lambda 更灵活,尽管恕我直言,有时它们的可读性不如方法引用(但这是一个品味问题,许多人认为相反)。
一种仍然使用方法引用的方法是使用辅助方法:
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));
但是,老实说,我更喜欢使用 lambda。