【问题标题】:Why Java's Optional doesn't call Consumer in ifPresent()?为什么 Java 的 Optional 不在 ifPresent() 中调用 Consumer?
【发布时间】:2017-03-18 02:20:44
【问题描述】:
public class TestSupplier {

Optional<Integer> opt1;

public static void main(String[] args) {
    // TODO Auto-generated method stub

    TestSupplier ts1 = new TestSupplier();

    ts1.opt1 = ts1.average(100,20,30,80);
    Consumer<Integer> cns1 = (x) -> x += 3;
    ts1.opt1.ifPresent(cns1);
    System.out.println(ts1.opt1.get());

}


private Optional<Integer> average(int... n1) {
    if (n1.length == 0) return Optional.empty();
    int sum = 0;
    for(int score: n1) sum += score; 
    return Optional.of(sum/n1.length);

}

}

当我运行代码时,结果为 57(即平均 100、20、30、80 的正确结果),但我创建了一个应将结果增加 3 的消费者……但它似乎不起作用。

有人可以帮我吗?

【问题讨论】:

  • "(x) -&gt; x += 3" 你认为你在那里增加了什么变量?
  • n1 是一个局部变量,那为什么cns1 会影响它的状态呢?
  • 你想做的是ts1.opt1.map(x -&gt; x + 3).get()

标签: java lambda java-8 optional consumer


【解决方案1】:

Consumer 操作实际上正在运行,但您提供的主体仅修改了最终丢失的本地实例。 ifPresent() 方法只能用于执行副作用(动作)。

如果您想对 Optional 实例保存的值执行计算,请改用 map()

ts1.opt1
  .map(x -> x + 3).orElseThrow(...)

记住在Optional 实例上使用get() 时要小心。在决定使用它之前,请先查看 orElseorElseGetorElseThrow

【讨论】:

  • 方法名为orElseThrow。关于get(),你也可以说orElseThrow“本质上将NullPointerException转化为”WhatEverYouWantException。所以你必须写orElseThrow(NoSuchElementException::new) 来摆脱未来的“弃用”警告。反正我一直用orElseThrow(NullPointerException::new)是件好事……
  • 感谢您指出错误的函数名称。如果您想要 NPE,请停止使用 Optionals。
  • 这有点讽刺,但无论如何,是否使用Optional 的选择并不总是你的,即Stream API 方法返回Optionals,而等效的Collection API 操作抛出NoSuchElementException 在类似的情况下,例如Collections.max(list, cmp)list.stream().maxBy(cmp).get() 具有相同的行为。我不相信弃用 get() 会这么有用。
  • 请注意,Optional.get 在 Java 9 中不会被弃用,但谨慎使用它的建议很好。
  • @StuartMarks 感谢您的澄清:) 我记得我读过这个mail.openjdk.java.net/pipermail/core-libs-dev/2016-April/…。我现在会更新我的答案
【解决方案2】:
Consumer<Integer> cns1 = new Consumer<Integer>() {
    public @Override void accept(Integer x) {
        // x is a local variable
        x += 3; // unboxing, adding, boxing 
        // the local variable has been changed
    }
};

正是这种情况,将 lambda 转换为匿名类完美地使这一切变得清晰易懂。

这里最好的方法是

ts1.opt1.map(x -> x + 3).ifPresent(System.out::println);

您可以使用可变类的实例(例如 AtomicInteger 类):

Consumer<AtomicInteger> cns1 = x -> x.addAndGet(3);

接受Consumer&lt;AtomicInteger&gt;后会改变状态(虽然不推荐,看@pivovarit的回答)。

还有一行

IntStream.of(100, 20, 30, 80).average().ifPresent(System.out::println);

可能会取代你所有的日常工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-02
    • 1970-01-01
    相关资源
    最近更新 更多