Java opcional - orElse () vs orElseGet ()
1. Introdução
A API deOptional normalmente tem dois métodos que podem causar confusão:orElse() andorElseGet().
Neste tutorial rápido, veremos a diferença entre os dois e exploraremos quando usar cada um.
2. Assinaturas
Vamos primeiro começar com o básico observando suas assinaturas:
public T orElse(T other)
public T orElseGet(Supplier extends T> other)
Claramente,orElse() aposta em qualquer parâmetro de um tipoT , enquantoorElseGet() aceita uma interface funcional do tipoSupplier que retorna um objeto do tipoT.
Agora, com base em seusJavadocs:
-
orElse(): retorna o valor se presente, caso contrário, retornaother
-
orElseGet(): retorna o valor se presente, caso contrário, invoqueother e retorne o resultado de sua invocação
3. Diferenças
É fácil ficar um pouco confuso com essas definições simplificadas, então vamos nos aprofundar um pouco mais em alguns cenários de uso reais.
3.1. orElse()
Assumindo que temos nossologger configurado corretamente, vamos começar escrevendo um código simples:
String name = Optional.of("example")
.orElse(getRandomName());
Observe quegetRandomName() é um método que retornaname de umList<String>ofnames: aleatório
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);
}
Ao executar nosso código, encontraremos abaixo as mensagens impressas no console:
getRandomName() method - start
getRandomName() method - end
A variávelname manterá “example” no final da execução do código.
Com ele, podemos facilmente inferir que oparameter of orElse() is evaluated even when having a non-empty Optional.
3.2. orElseGet()
Agora, vamos tentar escrever um código semelhante usandoorElseGet():
String name = Optional.of("example")
.orElseGet(() -> getRandomName());
O código acima não invocarágetRandomName() method.
Lembre-se (do Javadoc) que o método Supplier passado como um argumento só é executado quandoan Optional value não está presente.
UsarorElseGet() em nosso caso, portanto, nos economizará algum tempo envolvido no cálculo dename aleatório.
4. Medindo o impacto no desempenho
Agora, para entender também as diferenças de desempenho, vamos usarJMHe ver alguns números reais:
@Benchmark
@BenchmarkMode(Mode.AverageTime)
public String orElseBenchmark() {
return Optional.of("example").orElse(getRandomName());
}
EorElseGet():
@Benchmark
@BenchmarkMode(Mode.AverageTime)
public String orElseGetBenchmark() {
return Optional.of("example").orElseGet(() -> getRandomName());
}
Ao executar nossos métodos de benchmark, obtemos:
Benchmark Mode Cnt Score Error Units
orElseBenchmark avgt 20 60934.425 ± 15115.599 ns/op
orElseGetBenchmark avgt 20 3.798 ± 0.030 ns/op
Como podemos ver, o impacto no desempenho pode ser substancial, mesmo para um cenário de caso de uso tão simples.
Os números acima podem variar ligeiramente, no entanto,orElseGet() has clearly outperformed orElse() for our particular example.
Afinal,orElse() in envolve o cálculo do métodogetRandomName() para cada execução.
5. O que é importante?
Além dos aspectos de desempenho, outros fatores dignos de consideração envolvem:
-
E se o método executasse alguma lógica adicional? E.g. fazendo algumas inserções ou atualizações de banco de dados
-
Mesmo quando atribuímos um objeto aorElse() parameter:
String name = Optional.of("example").orElse("Other")
ainda estamos criando“Other” object sem motivo
E é por isso que é importante tomarmos uma decisão cuidadosa entreorElse() areiaorElseGet() dependendo de nossas necessidades -by default, it makes more sense to use orElseGet() every time unless the default object is already constructed and accessible directly.
6. Conclusão
Neste artigo, aprendemos as nuances entre os métodosOptional orElse() andOrElseGet() . Também observamos como algumas vezes esses conceitos simples podem ter um significado mais profundo.
Como sempre, o código-fonte completo pode ser encontradoover on Github.