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

Tem*_*ist 20 java foreach java-8 method-reference

假设我有以下功能界面:

public interface TemperatureObserver {
    void react(BigDecimal t);
}
Run Code Online (Sandbox Code Playgroud)

然后在另一个类中已经填充ArrayList了类型的对象TemperatureObserver.假设temp是a BigDecimal,我可以react使用以下命令在循环中调用:

observers.forEach(item -> item.react(temp));
Run Code Online (Sandbox Code Playgroud)

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

以下不起作用:

observers.forEach(TemperatureObserver::react);
Run Code Online (Sandbox Code Playgroud)

错误消息告诉我

  1. forEachArraylist observers该类型中不适用TemperatureObserver::react
  2. TemperatureObserver 没有定义方法 react(TemperatureObserver)

很公平,正如forEach预期的那样,作为一个参数a Consumer<? super TemperatureObserver>,我的界面尽管是功能性的,但Consumer由于react(BigDecimal在我的情况下)的不同参数而不符合.

那么这可以解决,还是一个lambda没有相应的方法参考的情况?

And*_*eas 21

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

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

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

    流对象成为this该方法的对象.

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

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

    class Other {
        void act(Observer o);
    }
    
    Other other = new Other();
    observers.forEach(other::act);
    
    observers.forEach(obs -> other.act(obs)); // equivalent lambda
    
    Run Code Online (Sandbox Code Playgroud)

还有一个构造函数引用,但这与此问题并不真正相关.

由于您有外部值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);
Run Code Online (Sandbox Code Playgroud)

  • @TempAgilist而Andreas就在这里,我怀疑使用一个类来包装一个值,*只是为了使用方法引用*会添加任何值; 除了未来的维护者很难理解. (7认同)
  • @Eugene:同意了.您的评论也代表了Federico(见下文)使用辅助方法的有效解决方案.我只是想知道我是否错过了使用方法引用的简单解决方案.现在我知道我没有:) (2认同)

Fed*_*ner 9

请查看Java Tutorial中的Method References部分.它说:

有四种方法参考:

  • 参考静态方法: ContainingClass::staticMethodName

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

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

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

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

(TemperatureObserver item) -> item.react()
Run Code Online (Sandbox Code Playgroud)

要不就:

item -> item.react()
Run Code Online (Sandbox Code Playgroud)

哪个与您的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);
}
Run Code Online (Sandbox Code Playgroud)

你可以使用如下:

observers.forEach(consumingParam(TemperatureObserver::react, temp));
Run Code Online (Sandbox Code Playgroud)

但是,老实说,我更喜欢使用lambda.


Bor*_*aze 6

它不起作用,因为您遍历处理程序而不是参数.

例如,此代码有效:

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

    TemperatureObserver observer = new TemperatureObserverImpl();

    temps.forEach(observer::react);
Run Code Online (Sandbox Code Playgroud)