Java 8 中带有实例方法引用的 forEach 的局限性

Limitations of forEach with instance method references in Java 8

假设我有以下功能接口:

public interface TemperatureObserver {
    void react(BigDecimal t);
}

然后在另一个 class 中已经填充了 ArrayList 类型 TemperatureObserver 的对象。 假设 tempBigDecimal,我可以使用以下循环调用 react

observers.forEach(item -> item.react(temp));

我的问题:我可以为上面的代码使用方法参考吗?

以下无效:

observers.forEach(TemperatureObserver::react);

错误消息告诉我

  1. forEach中的Arraylist observers不适用于TemperatureObserver::react
  2. 类型
  3. 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);

当流中的单个值可用时,可以使用三种方法引用:

  1. 流对象的无参数方法。

    class Observer {
        public void act() {
            // code here
        }
    }
    
    observers.forEach(Observer::act);
    
    observers.forEach(obs -> obs.act()); // equivalent lambda
    

    流式对象成为方法的 this 对象。

  2. 以流式对象作为参数的静态方法。

    class Other {
        public static void act(Observer o) {
            // code here
        }
    }
    
    observers.forEach(Other::act);
    
    observers.forEach(obs -> Other.act(obs)); // equivalent lambda
    
  3. 以流对象作为参数的非静态方法。

    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。