在 Java 8 中使用实例方法引用的 forEach 的限制

2022-09-02 03:19:42

假设我有以下功能接口:

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

然后在另一个类中,已经填充了类型为 的对象。假设这是一个,我可以在循环中使用以下命令调用:ArrayListTemperatureObservertempBigDecimalreact

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

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

以下情况不起作用:

observers.forEach(TemperatureObserver::react);

错误消息告诉我

  1. forEach中不适用类型Arraylist observersTemperatureObserver::react
  2. TemperatureObserver不定义方法react(TemperatureObserver)

公平地说,正如预期的那样,作为参数a,我的接口虽然是功能性的,但由于(在我的情况下是a)的不同参数而不符合。forEachConsumer<? super TemperatureObserver>ConsumerreactBigDecimal

那么这可以解决吗,或者这是lambda没有相应方法参考的情况?


答案 1

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

  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);

答案 2

请查看 Java 教程中的“方法参考”部分。它说:

有四种方法引用:

  • 对静态方法的引用:ContainingClass::staticMethodName

  • 对特定对象的实例方法的引用:containingObject::instanceMethodName

  • 引用特定类型的任意对象的实例方法:ContainingType::methodName

  • 对构造函数的引用:ClassName::new

在那里,它解释了即 将是第三种类型的方法引用:对特定类型的任意对象的实例方法的引用。在调用该方法的上下文中,该方法引用将等效于以下 lambda 表达式:TemperatureObserver::reactStream.forEach

(TemperatureObserver item) -> item.react()

或者只是:

item -> item.react()

这与您的方法签名不匹配。void TemperatureObserver.react(BigDecimal t)

正如您已经怀疑的那样,在某些情况下,您无法找到 lambda 的等效方法引用。Lambdas更加灵活,尽管恕我直言,有时它们的可读性不如方法引用(但这是一个品味问题,许多人认为相反)。

仍然使用方法引用的一种方法是使用帮助器方法:

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。