1. Introduction
In this article, we will look at getting value out of Optional. There are 5 methods provided in Optional class which must be used to get value out of Optional. We will cover all methods in this article.
2. orElse family
orElse method family provides several methods to get value out of Optional. 2 different variants are getting default value or throwing an exception if value is not present in Optional.
- orElse(T defaultValue)
- orElseGet(Supplier<? extends T> supplier)
- orElseThrow​(Supplier<? extends X> exceptionSupplier)
- orElseThrow​()
- or(Supplier<? extends Optional<? extends T>> supplier)
Our use-case for this article is, given List<Transaction> and InvoiceId, get the specified InvoiceId’s total payment.
Data structure is like this : Transaction has an Invoice. Invoice has InvoiceId and InvoiceTotal(money object).
Invoice invoice = transaction.invoice(); InvoiceId invoiceId = invoice.invoiceId(); Money totalInvoice = invoice.invoiceTotal();
Let us take a look at these methods.
3. orElse(T defaultValue)
orElse method is one of the most simplest methods of getting value out of Optional. orElse method returns the value from Optional if value is present else it returns the default value passed in its parameter.
The below method demonstrate the use of java.util.Stream and Optional methods. We take in List<Transaction> and InvoiceId. Out expected output is get the invoice total of the invoiceId passed in parameter. We stream through the transactions and check for required invoice id, if found we then get invoice’s total value. findAny() method returns an Optional<Money>. This object may contain invoice total if the stream operation had found the specified invoiceId else Optional would be empty.
If the invoice total is present then orElse method will return that value else it will return default values provided by us which is “USD0.00”.
Money orElseDemo(List<Transaction> txns, InvoiceId invoiceId) { Optional<Money> optMoney = txns.stream() .map(txn -> txn.invoice()) .filter(invoice -> invoice.invoiceId() .equals(invoiceId)) .map(invoice -> invoice.invoiceTotal()) .findAny(); return optMoney.orElse(Money.zero(CurrencyUnit.USD)); }
4. orElseGet(Supplier<? extends T> supplier)
orElseGet method is similar to orElse method. The only difference is orElseGet method accepts Supplier of result. orElse method accepts result itself.
So the question might arise why provide Supplier instead of providing result itself.
In orElse method we have to provide a default result object. This default result object needs to be created and then passed as an argument. For method orElseGet, if the value is absent in Optional then the orElseGet method will invoke the Supplier and return result of its invocation.
Money orElseGetDemo(List<Transaction> txns, InvoiceId invoiceId) { Optional<Money> optMoney = txns.stream() .map(txn -> txn.invoice()) .filter(invoice -> invoice.invoiceId() .equals(invoiceId)) .map(invoice -> invoice.invoiceTotal()) .findAny(); return optMoney.orElseGet(() -> Money.zero(CurrencyUnit.USD)); }
5. orElseThrow​(Supplier<? extends X> exceptionSupplier)
orElseThrow(exceptionSupplier) method returns the value, if present, else throws an exception provided in Supplier. This method can throw NullPointerException, if the value is not present and exceptionSupplier is passed as null.
This method accepts Supplier of InvoiceIdNotFoundException. It is good practice to use a Supplier as the instance of any Exception class because it the instance of the class is created lazily. Creating instance of Exception classes are not lightweight as they call super classes and captures entire stacktrace.
Money orElseThrowDemo(List<Transaction> txns, InvoiceId invoiceId) { Optional<Money> optMoney = txns.stream() .map(txn -> txn.invoice()) .filter(invoice -> invoice.invoiceId() .equals(invoiceId)) .map(invoice -> invoice.invoiceTotal()) .findAny(); return optMoney.orElseThrow(InvoiceIdNotFoundException::new); }
6. orElseThrow​()
orElseThrow() method is used to return the value, if present, else it will throw NoSuchElementException.
Money orElseThrowDemo(List<Transaction> txns, InvoiceId invoiceId) { Optional<Money> optMoney = txns.stream() .map(txn -> txn.invoice()) .filter(invoice -> invoice.invoiceId() .equals(invoiceId)) .map(invoice -> invoice.invoiceTotal()) .findAny(); return optMoney.orElseThrow(); }
7. or(Supplier<? extends Optional<? extends T>> supplier)
or() method accepts a Supplier of Optional. If the value in this Optional is present then this Optional is returned else it returns Optional generated by Supplier.
Optional<String> o1 = Optional.of("abc"); o1 = o1.or(() -> Optional.of("def")); System.out.println(o1); //Prints Optional[abc] Optional<String> o2 = Optional.empty(); Optional<String> o3 = o2.or(() -> Optional.of("def")); System.out.println(o3); //Prints Optional[def]
8. Which method to use?
Judgement to use either of these methods should be left up to the use-case. I am providing a rule of thumb that can be used as decision maker in terms of selecting when to use which method.
Method | Scenario |
orElse(T defaultValue) | If default object is already created and accessible to you then use this method. |
orElseGet(Supplier<? extends T> supplier) | If default object is not created and not accessible to you and/or creating the object can have a performance impact, then use this method. Performance impact must be measured using benchmarks. |
Which using methods that throw exception keep in mind that throwing an exception is expensive because the entire stack trace is captured when an exception is created.
Method | Scenario |
orElseThrow​(Supplier<? extends X> exceptionSupplier) | You want to throw a custom exception. |
orElseThrow() | You want to throw NoSuchElementException. |
8. Conclusion
In this article, we discussed 6 different methods which can be used to return value out of Optional, if present. I have also provided 4 different scenarios under which this methods should be used. Remember this scenarios should be considered as a rule of thumb and not a rule. Keep in mind throwing exception is expensive so be careful in doing so.