`Optional.orElse()`和`Optional.orElseGet()`之间的区别

问题描述 投票:160回答:6

我试图了解Optional<T>.orElse()Optional<T>.orElseGet()方法之间的区别。

orElse()方法的描述是“如果存在则返回值,否则返回其他值”。

虽然,orElseGet()方法的描述是“如果存在则返回值,否则调用other并返回该调用的结果”。

orElseGet()方法采用供应商功能接口,基本上不接受任何参数并返回T

在哪种情况下你需要使用orElseGet()?如果你有一个方法T myDefault()你为什么不只是做optional.orElse(myDefault())而不是optional.orElseGet(() -> myDefault())

似乎orElseGet()没有将lambda表达式的执行推迟到以后的某个时间或什么的,所以它有什么意义呢? (我本以为如果它返回一个更安全的Optional<T>更有用,其get()从不投掷NoSuchElementExceptionisPresent()总是返回真实......但显然它不是,它只是返回TorElse())。

我还缺少其他一些差异吗?

java java-8 optional
6个回答
141
投票

采取以下两种情况:

Optional<Foo> opt = ...
Foo x = opt.orElse( new Foo() );
Foo y = opt.orElseGet( Foo::new );

如果opt不包含值,则两者确实等价。但是如果opt确实包含一个值,那么将创建多少个Foo对象?

P.s。:当然在本例中,差异可能无法衡量,但如果您必须从远程Web服务或数据库中获取默认值,则突然变得非常重要。


72
投票

简答:

  • 无论Optional.isPresent()值如何,orElse()总是会调用给定的函数,无论你是否需要它
  • orElseGet()只会在Optional.isPresent() == false时调用给定的函数

在实际代码中,当需要的资源很昂贵时,您可能需要考虑第二种方法。

// Always get heavy resource
getResource(resourceId).orElse(getHeavyResource()); 

// Get heavy resource when required.
getResource(resourceId).orElseGet(() -> getHeavyResource()) 

有关更多详细信息,请考虑以下使用此函数的示例:

public Optional<String> findMyPhone(int phoneId)

区别如下:

                           X : buyNewExpensivePhone() called

+——————————————————————————————————————————————————————————————————+——————————————+
|           Optional.isPresent()                                   | true | false |
+——————————————————————————————————————————————————————————————————+——————————————+
| findMyPhone(int phoneId).orElse(buyNewExpensivePhone())          |   X  |   X   |
+——————————————————————————————————————————————————————————————————+——————————————+
| findMyPhone(int phoneId).orElseGet(() -> buyNewExpensivePhone()) |      |   X   |
+——————————————————————————————————————————————————————————————————+——————————————+

optional.isPresent() == false,两种方式之间没有区别。然而,当optional.isPresent() == trueorElse()总是调用后续函数,无论你是否想要它。

最后,使用的测试用例如下:

结果:

------------- Scenario 1 - orElse() --------------------
  1.1. Optional.isPresent() == true
    Going to a very far store to buy a new expensive phone
    Used phone: MyCheapPhone

  1.2. Optional.isPresent() == false
    Going to a very far store to buy a new expensive phone
    Used phone: NewExpensivePhone

------------- Scenario 2 - orElseGet() --------------------
  2.1. Optional.isPresent() == true
    Used phone: MyCheapPhone

  2.2. Optional.isPresent() == false
    Going to a very far store to buy a new expensive phone
    Used phone: NewExpensivePhone

码:

public class TestOptional {
    public Optional<String> findMyPhone(int phoneId) {
        return phoneId == 10
                ? Optional.of("MyCheapPhone")
                : Optional.empty();
    }

    public String buyNewExpensivePhone() {
        System.out.println("\tGoing to a very far store to buy a new expensive phone");
        return "NewExpensivePhone";
    }


    public static void main(String[] args) {
        TestOptional test = new TestOptional();
        String phone;
        System.out.println("------------- Scenario 1 - orElse() --------------------");
        System.out.println("  1.1. Optional.isPresent() == true");
        phone = test.findMyPhone(10).orElse(test.buyNewExpensivePhone());
        System.out.println("\tUsed phone: " + phone + "\n");

        System.out.println("  1.2. Optional.isPresent() == false");
        phone = test.findMyPhone(-1).orElse(test.buyNewExpensivePhone());
        System.out.println("\tUsed phone: " + phone + "\n");

        System.out.println("------------- Scenario 2 - orElseGet() --------------------");
        System.out.println("  2.1. Optional.isPresent() == true");
        // Can be written as test::buyNewExpensivePhone
        phone = test.findMyPhone(10).orElseGet(() -> test.buyNewExpensivePhone());
        System.out.println("\tUsed phone: " + phone + "\n");

        System.out.println("  2.2. Optional.isPresent() == false");
        phone = test.findMyPhone(-1).orElseGet(() -> test.buyNewExpensivePhone());
        System.out.println("\tUsed phone: " + phone + "\n");
    }
}

56
投票

我到达这里是因为Kudo提到的问题。

我正在为别人分享我的经验。

orElse, or orElseGet, that is the question:

static String B() {
    System.out.println("B()...");
    return "B";
}

public static void main(final String... args) {
    System.out.println(Optional.of("A").orElse(B()));
    System.out.println(Optional.of("A").orElseGet(() -> B()));
}

版画

B()...
A
A

orElse与可选项的值相互依赖地计算B()的值。因此,orElseGet是懒惰的。


35
投票

我想说orElseorElseGet之间的最大区别在于我们想要评估一些东西来获得else条件下的新值。

考虑这个简单的例子 -

// oldValue is String type field that can be NULL
String value;
if (oldValue != null) {
    value = oldValue;
} else {
    value = apicall().value;
}

现在让我们将上面的例子转换为使用OptionalorElse

// oldValue is Optional type field
String value = oldValue.orElse(apicall().value);

现在让我们将上面的例子转换为使用OptionalorElseGet

// oldValue is Optional type field
String value = oldValue.orElseGet(() -> apicall().value);

当调用orElse时,将评估apicall().value并将其传递给该方法。然而,在orElseGet的情况下,只有在oldValue为空时才会进行评估。 orElseGet允许懒惰评估。


2
投票

以下示例应说明差异:

String destroyTheWorld() {
  // destroy the world logic
  return "successfully destroyed the world";
}

Optional<String> opt = Optional.empty();

// we're dead
opt.orElse(destroyTheWorld());

// we're safe    
opt.orElseGet(() -> destroyTheWorld());

答案也出现在文档中。

public T orElseGet(Supplier<? extends T> other)

返回值(如果存在),否则调用other并返回该调用的结果。

如果Supplier出现,将不会援引Optional。然而,

public T orElse(T other)

如果存在则返回值,否则返回其他值。

如果other是一个返回字符串的方法,它将被调用,但是如果Optional存在则不会返回它的值。


0
投票

考虑以下代码:

import java.util.Optional;

// one class needs to have a main() method
public class Test
{
  public String orelesMethod() {
    System.out.println("in the Method");
    return "hello";
  }

  public void test() {
    String value;
    value = Optional.<String>ofNullable("test").orElseGet(this::orelesMethod);
    System.out.println(value); 

    value = Optional.<String>ofNullable("test").orElse(orelesMethod());
    System.out.println(value); 
  }

  // arguments are passed using the text field below this editor
  public static void main(String[] args)
  {
    Test test = new Test();

    test.test();
  }
}

如果我们以这种方式获得valueOptional.<String>ofNullable(null),orElseGet()和orElse()之间没有区别,但是如果我们以这种方式获得valueOptional.<String>ofNullable("test")中的orelesMethod()orElseGet()将不被调用,但是在orElse()中它将被调用

© www.soinside.com 2019 - 2024. All rights reserved.