1. Введение
В API option
обычно есть два метода, которые могут вызвать путаницу: orElse()
и orElseGet()
.
В этом кратком руководстве мы рассмотрим разницу между ними и узнаем, когда использовать каждый из них.
2. Подписи
Во-первых, давайте начнем с основ, взглянув на их подписи:
public T orElse(T other)
public T orElseGet(Supplier<? extends T> other)
Понятно, что orElse()
принимает любой параметр типа T,
тогда как orElseGet()
принимает функциональный интерфейс типа Supplier
, который возвращает объект типа T.
На основе их Javadocs :
orElse()
: возвращает значение, если оно присутствует, в противном случае возвращаетдругое
orElseGet():
возвращает значение, если оно присутствует, в противном случае вызываетдругое
и возвращает результат своего вызова.
3. Отличия
Эти упрощенные определения легко запутать, поэтому давайте копнем немного глубже и рассмотрим некоторые реальные сценарии использования.
3.1. илииначе()
Предполагая, что наш регистратор настроен правильно, давайте начнем с написания простого фрагмента кода:
String name = Optional.of("foreach")
.orElse(getRandomName());
Обратите внимание, что метод getRandomName()
возвращает случайное имя
из списка имен List<String>
:
public String getRandomName() {
LOG.info("getRandomName() method - start");
Random random = new Random();
int index = random.nextInt(5);
LOG.info("getRandomName() method - end");
return names.get(index);
}
При выполнении нашего кода мы обнаружим следующие сообщения, напечатанные в консоли:
getRandomName() method - start
getRandomName() method - end
Имя
переменной будет содержать «foreach»
в конце выполнения кода.
С его помощью мы можем легко сделать вывод, что параметр orElse()
оценивается, даже если у нас есть непустой Optional
.
3.2. orElseGet()
Теперь давайте попробуем написать аналогичный код, используя orElseGet()
:
String name = Optional.of("foreach")
.orElseGet(() -> getRandomName());
Приведенный выше код не вызывает метод getRandomName()
.
Помните (из Javadoc), что метод Supplier
, переданный в качестве аргумента, выполняется только в том случае , если необязательное
значение отсутствует.
Поэтому использование orElseGet()
для нашего случая сэкономит нам время, затрачиваемое на вычисление случайного имени
.
4. Измерение влияния на производительность
Теперь, чтобы также понять различия в производительности, давайте воспользуемся JMH и посмотрим некоторые реальные цифры:
@Benchmark
@BenchmarkMode(Mode.AverageTime)
public String orElseBenchmark() {
return Optional.of("foreach").orElse(getRandomName());
}
И orElseGet()
:
@Benchmark
@BenchmarkMode(Mode.AverageTime)
public String orElseGetBenchmark() {
return Optional.of("foreach").orElseGet(() -> getRandomName());
}
При выполнении наших тестовых методов мы получаем:
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()
в нашем конкретном примере.
В конце концов, orElse()
включает в себя вычисление метода getRandomName()
для каждого запуска.
5. Что важно?
Помимо аспектов производительности, другие факторы, которые стоит учитывать, включают:
- Что, если метод будет выполнять некоторую дополнительную логику? Например, сделать некоторые вставки или обновления БД
- Даже когда мы назначаем объект параметру
orElse()
, мы все равно создаем объект«Другой»
без всякой причины:
String name = Optional.of("foreach").orElse("Other")
Вот почему для нас важно сделать тщательный выбор между orElse()
и orElseGet()
в зависимости от наших потребностей. По умолчанию имеет смысл использовать orElseGet()
каждый раз, если только объект по умолчанию уже не создан и не доступен напрямую.
6. Заключение
В этой статье мы узнали о нюансах между методами Optional orElse()
и OrElseGet()
. Мы также обсудили, как такие простые понятия иногда могут иметь более глубокое значение.
Как всегда, полный исходный код можно найти на Github .