Разница между `Optional.orElse()` и `Optional.orElseGet()`

Я пытаюсь понять разницу между методами Optional<T>.orElse() и Optional<T>.orElseGet().

Описание метода orElse(): "Вернуть значение, если оно присутствует, иначе вернуть другое".

В то время как описание метода orElseGet() - "Возвратить значение, если оно присутствует, иначе вызвать другое и вернуть результат этого вызова".

Метод orElseGet() использует функциональный интерфейс поставщика, который по существу не принимает никаких параметров и возвращает T.

В какой ситуации вам понадобится использовать orElseGet()? Если у вас есть метод T myDefault(), почему бы вам просто не сделать optional.orElse(myDefault()), а не optional.orElseGet(() -> myDefault())?

Кажется, что orElseGet() откладывает выполнение выражения лямбды до какого-то более позднего времени или что-то в этом роде, и что это за точка? (Я бы подумал, что было бы более полезно, если бы он вернул более безопасный Optional<T>, который get() никогда не выбрасывает NoSuchElementException и isPresent() всегда возвращает true... но, очевидно, его нет, он просто возвращает T например orElse()).

Есть ли какая-то другая разница, которую мне не хватает?

Ответы

Ответ 1

Возьмите эти два сценария:

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

Если opt не содержит значения, они действительно эквивалентны. Но если opt содержит значение, сколько объектов Foo будет создано?

Ps: конечно, в этом примере разница, вероятно, не будет измерима, но если вам нужно получить значение по умолчанию из удаленного веб-сервиса, например, или из базы данных, это внезапно становится очень важным.

Ответ 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() == false, между двумя способами нет никакой разницы. Однако, когда orElse() optional.isPresent() == true, orElse() всегда вызывает последующую функцию, хотите вы этого или нет.

И наконец, используемый тестовый пример, как показано ниже:

Результат:

------------- 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");
    }
}

Ответ 3

Я потянулся сюда за проблемой, о которой говорил Кудо.

Я делюсь своим опытом с другими.

orElse или orElseGet, вот в чем вопрос:

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 ленив.

Ответ 4

Я бы сказал, что самое большое различие между orElse и orElseGet происходит, когда мы хотим что-то оценить, чтобы получить новое значение в else.

Рассмотрим этот простой пример -

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

Теперь перейдем к приведенному выше примеру к использованию Optional вместе с orElse,

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

Теперь измените приведенный выше пример на использование Optional вместе с orElseGet,

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

Когда вызывается orElse, вычисляется apicall().value и передается методу. Принимая во внимание, что в случае orElseGet оценка выполняется, только если oldValue пуст. orElseGet позволяет ленивую оценку.

Ответ 5

Разница довольно тонкая, и если вы не будете обращать особого внимания, вы будете использовать ее неправильно.

Лучший способ понять разницу между orElse() и orElseGet() состоит в том, что orElse() всегда будет выполняться, если Optional<T> равен нулю или не, но orElseGet() будет выполняется только тогда, когда Optional<T> равен нулю.

В словаре значение orElse равно : - выполнять деталь, когда чего-то нет, но здесь это противоречит, см. пример ниже:

    Optional<String> nonEmptyOptional = Optional.of("Vishwa Ratna");
    String value = nonEmptyOptional.orElse(iAmStillExecuted());

    public static String iAmStillExecuted(){
    System.out.println("nonEmptyOptional is not NULL,still I am being executed");
    return "I got executed";
    }

Вывод: nonEmptyOptional не равен NULL, но я все еще выполняюсь


    Optional<String> emptyOptional = Optional.ofNullable(null);
    String value = emptyOptional.orElse(iAmStillExecuted());
    public static String iAmStillExecuted(){
    System.out.println("emptyOptional is NULL, I am being executed, it is normal as 
    per dictionary");
    return "I got executed";
    }

Выходные данные: emptyOptional НЕДЕЙСТВИТЕЛЕН, я выполняюсь, это нормально согласно                 словарь

Для orElseGet(), метод идет в соответствии со значением словаря, Партия orElseGet() будет выполняться только когда опционально нулевой.

Контрольные показатели:

+--------------------+------+-----+------------+-------------+-------+
| Benchmark          | Mode | Cnt | Score      | Error       | Units |
+--------------------+------+-----+------------+-------------+-------+
| orElseBenchmark    | avgt | 20  | 60934.425  | ± 15115.599 | ns/op |
+--------------------+------+-----+------------+-------------+-------+
| orElseGetBenchmark | avgt | 20  | 3.798      | ± 0.030     | ns/op |
+--------------------+------+-----+------------+-------------+-------+

Замечания: orElseGet() явно превзошел orElse() для нашего конкретного примера.

Надеюсь, это прояснит сомнения таких людей, как я, которые хотят получить самый простой пример из жизни :)

Ответ 6

Следующий пример должен продемонстрировать разницу:

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 существует.

Ответ 7

Учитывая следующий код:

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();
  }
}

если мы получим value следующим образом: Optional.<String>ofNullable(null), нет разницы между orElseGet() и orElse(), но если мы получим value таким образом: Optional.<String>ofNullable("test"), orelesMethod() в orElseGet() не будет вызываться, но в orElse() он будет вызываться