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

时间:2017-05-13 20:27:14

标签: java foreach java-8 method-reference

假设我有以下功能界面:

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

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

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

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

以下不起作用:

observers.forEach(TemperatureObserver::react);

错误消息告诉我

    forEach中的
  1. Arraylist observers不适用于TemperatureObserver::react
  2. 类型
  3. TemperatureObserver未定义方法react(TemperatureObserver)
  4. 很公平,因为forEach期望作为参数Consumer<? super TemperatureObserver>,我的界面虽然功能正常但不符合Consumer,因为react的参数不同(在我的情况下是BigDecimal)。

    这样可以解决,还是lambda没有相应的方法参考?

3 个答案:

答案 0 :(得分:20)

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

  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
    
  4. 还有一个构造函数引用,但这与此问题并不相关。

    由于您有一个外部值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);
    

答案 1 :(得分:9)

看看Method References section in the Java Tutorial。它说:

  

有四种方法参考:

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

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

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

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

  •   

在那里它解释了ie 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。

答案 2 :(得分:6)

它不起作用,因为你迭代处理程序而不是参数。

例如,此代码有效:

    ArrayList<BigDecimal> temps = new ArrayList<>();

    TemperatureObserver observer = new TemperatureObserverImpl();

    temps.forEach(observer::react);