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

2022-08-31 05:21:33

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

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

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

该方法采用 Supplier 功能接口,该接口实质上不采用任何参数并返回 。orElseGet()T

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

这似乎并没有将lambda表达式的执行推迟到以后的某个时间或什么时间,那么它有什么意义呢?(我本来以为,如果它返回一个更安全的人,它永远不会抛出并且总是返回真实,那会更有用......但显然它不是,它只是像)orElseGet()Optional<T>get()NoSuchElementExceptionisPresent()TorElse()

我是否错过了其他差异?


答案 1

以以下两种方案为例:

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

如果不包含值,则两者确实是等效的。但是,如果 包含值,将创建多少个对象?optoptFoo

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


答案 2

简短的回答:

  • orElse() 将始终调用给定的函数,无论您是否愿意,无论其值如何Optional.isPresent()
  • 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() == falseoptional.isPresent() == trueorElse()

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

结果:

------------- Scenario 1 - orElse() --------------------
  1.1. Optional.isPresent() == true (Redundant call)
    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 (Redundant call)");
        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");
    }
}

推荐