У меня были проблемы с оценкой дополнительных возможностей Java. Рассмотрим следующий тест:

@Test
public void test() {
    System.out.println("GOT STRING: " + first().orElse(second()));
}

private Optional<String> first() {
    System.out.println("Evaluating first");
    return Optional.of("STRING OPTIONAL");
}

private String second() {
    System.out.println("Evaluating second");
    return "SECOND STRING";
}

Я ожидаю, что, поскольку first () возвращает непустой Optional, второй не оценивается. Но на самом деле результат такой:

Evaluating first
Evaluating second
GOT STRING: STRING OPTIONAL

Пробуем использовать функцию, используемую в orElse, в тесте:

@Test
public void test2() {
    System.out.println("GOT STRING: " + (first() != null ? "FIRST IS NOT NULL" : second()));
}

Выход:

Evaluating first
GOT STRING: FIRST IS NOT NULL

Почему оценивается второй вариант? Вроде плохо?

6
Dennis Ich 26 Апр 2016 в 12:57

3 ответа

Лучший ответ

Почему оценивается второй вариант?

Потому что вы вызываете метод second(), чтобы предоставить значение в качестве аргумента для orElse(). Значение аргумента будет проигнорировано (потому что у вас уже есть значение), но это не значит, что его не нужно указывать.

В основном это код:

first().orElse(second())

Эквивалентно:

Optional<String> tmp1 = first();
Optional<String> tmp2 = second();
Optional<String> result = tmp1.orElse(tmp2);

Здесь нет ничего особенного в необязательном порядке - именно так в Java работает оценка аргументов всегда .

Обратите внимание, что это большая разница между конструкцией library (orElse) и конструкцией оператора условия language (?:), которая оценивает только либо второй или третий операнд, но не оба одновременно.

Как упоминал Артур, если вы хотите отложить вызов second(), вам нужно использовать orElseGet, где переданный вами аргумент не является значением, которое следует использовать, если first не имеет один, но вызов, который нужно сделать, чтобы получить значение.

7
Jon Skeet 26 Апр 2016 в 10:01

Поскольку вызов second() передается как параметр методу orElse, и параметры быстро оцениваются в Java.

Если вам нужно предоставить поставщика или избежать нетерпеливого исполнения, вы можете использовать orElseGet, который ожидает Supplier

optional.orElseGet(()->second())) 
2
Sleiman Jneidi 26 Апр 2016 в 10:04

Он оценивается, потому что вы передаете значение second() по значению, а не просто передаете ссылку на саму функцию. Вам нужно сделать одно из следующих

System.out.println("GOT STRING: " + first().orElseGet(() ->second()));
System.out.println("GOT STRING: " + first().orElseGet(this::second));

Отложить оценку до тех пор, пока она действительно не понадобится.

10
Alexis C. 26 Апр 2016 в 16:10