It doesn't matter how much time you have been programming, NullPointerException is one of the most frustrating problems you'll face.
The Optional class was introduced in Java 8 to tackle the problem of NullPointerException's.
The java.util.Optional class is a generic type class that contains only one value of type T. Its purpose is to provide a safer alternative to reference objects of a type T that can be null. But optional objects are safe only when used the right way.
Let's see the ways you can create optional objects
Empty Optional
Using the Optional.empty() we can create an optional object which is empty of any type
Java
//returning a empty optional of type string
public static Optional<String> emptyOptional(){
return Optional.empty();
}
//or
Optional<String> str = Optional.empty();
Nullable Optional
Using the Optional.ofNullable() method we can create nullable optional objects. These objects can be null or have values in them
Java
public static Optional<Wallet> getWallet(){
return Optional.ofNullable(null);
}
Non-Nullable Optional
Using the Optional.of() method we can create non-nullable optional objects.
Java
public static Optional<Wallet> getWallet(){
return Optional.of(new Wallet(100));
}
Now let's look at different methods present in the Optional class that we can leverage to write clean code promoting null safety.
The get() method in the optional class gets the value if it exists or throws a NoSuchElementException
For example, let's say we have an object Wallet with private filed money. To get the money value we can do as below
Java
//normal way to get money
int savings = wallet.getMoney();
//when wallet object is wrapped in a optional
int savings = optionalWallet.get().getMoney();
If the wallet object is null the second way is safer than the first way as it thorws NoSuchElementException instead of NullPointerException
We can improve the second way by using the isPresent() method which returns a boolean for whether the value is present or not.
Java
List<Integer> savings = new ArrayList();
if(optionalWallet.isPresent()){
savings.add(optionalWallet.get().getMoney());
}
But at the same time, this way of doing is not less verbose than checking for not null.
Java
List<Integer> savings = new ArrayList();
if(wallet != null){
savings .add(wallet.getMoney());
}
To make the code more concise and clear, we can use ifPresent() method which takes a consumer or a runnable, or both. Using ifPresent() we can either consume a correct value or produce an alternative. Let's see how
Java
List<Integer> savings = new ArrayList();
optionalWallet.ifPresent(wallet -> savings.add(wallet.getMoney()));
In the above example, money is added to savings only when the wallet is not null i.e., when it is present.
In case we were adding the wallet itself to the list
Java
//using lambda
List<Wallet> wallets = new ArrayList<>();
optionalWallet.ifPresent(wallet -> wallets.add(wallet));
//you can also use method reference
List<Wallet> wallets = new ArrayList<>();
optionalWallet.ifPresent(wallets::add);
As you can see it is much better than the examples in the beginning.
There can be some scenarios where you might want to have a default value when the optional is empty, using the orElse() method we can produce an alternative value when the optional is null. Let's see an example
Java
List<Wallet> wallets = new ArrayList<>();
Wallet wallet = optionalWallet.orElse(new Wallet(0));
wallets.add(wallet);
In the above example, if the optionalWallet contains a value it is assigned to the wallet variable if not a default wallet with value 0 is assigned. The orElse() method can be used with any other type to produce default values.
You can also invoke a block of code inside the orElseGet() method to calculate a default value
Java
//calculating a default value
Wallet wallet = optionalWallet.orElseGet(() -> {
int randomAmount = (int) (Math.random() * 500); // Generating a random amount between 0 and 500
return new Wallet(randomAmount);
});
When working with spring boot applications and a database, when we make a select query to get a user, it is a good practice to wrap the user object around an optional, then we can do something like this
Java
//Getting the user from database
Optional<User> findUserById(String id);
//Calling the find user method
User user = findUserById(id)
.orElseThrow(UserNotFoundException::new);
In the above example, you can see, we didn't have to do any checks whether the user is not or not. If the user is present in the database we'll get the user, if not we gracefully throw an exception then and there itself.
These were some better ways to use the Optionals in Java. If you found this helpful post consider following and leave a clap.
Top comments (0)