Oogway's previous talk clears the confusions about Why Optional is added on java8? But PO is a Dragon warrior he is the finest Java warrior so he wants more, He wants to know when is the right time to use Optional. What are the best practices so he again went to Oogways and I am very lucky PO called me to take the note of the talk.
Here is the Conversation.
PO: Master Oogway, I completely understood why we use Optional, the crux of the Optional is -- it gives the caller a hint that output may not be available so, design your code accordingly. So it is a Conceptual improvement which force caller to tackle maybe scenario and the outcome -- less null pointer exception. But How to use it efficiently.
Oogways: PO, Listen carefully Optional is created to check a return value is present or not, So it is the one and the only purpose you should use Optional nothing else. Optional act as a container it wraps the return value and then applies different functions on it to determine value is present or not in an advance if the value is present it can take necessary functions on it in a functional way. but whatever the case Optional must be used with method return type. Use Optional<T> as a Composition or pass it is an input is a very lame idea and should be avoided.
PO: What is the problem to pass Optional as an input suppose I have a program to search a name so if I pass the name as Optional<String> developer don't have to do the null check.
see the below program.
package com.example.optional;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class OptionalTest {
private static List<String>nameList = new ArrayList<String>();
static {
nameList.add("shamik");
nameList.add("samir");
nameList.add("swastika");
}
Optional<String> findName(Optional<String> name){
return name.isPresent()?Optional.of(nameList.get(nameList.indexOf(name.get().toLowerCase()))):Optional.empty();
}
public static void main(String[] args) {
OptionalTest optionalTest = new OptionalTest();
Optional<String> searchedNameOptional = optionalTest.findName(Optional.of("Shamik"));
Optional<String> searchedNameOptionalSecond = optionalTest.findName(Optional.ofNullable(null));
searchedNameOptional.ifPresent(System.out::println);
searchedNameOptionalSecond.ifPresent(System.out::println);
}
}
Here, I create a method called findName which takes an Optional<String >, So that developer can check if the value is present or not if present same returns an Otional<String> else returns an Empty optional, So no null check involved and passing Optional caller signal to the developer that passing parameter may be present or absent so deal accordingly. Nice way to tackle input parameters. Master then why you are telling passing an Optional in the input is a bad idea?
Oogways: PO, There is a subtle conceptual error in your thinking,
You are right Optional is used for signaling value can be present or absent, But think about who signaling to whom, here caller signaling to the author of the code, The author is the creator of the code, author is very sure about nature of method input and returns value. As he wrote the method. So here signalling is meaningless, if caller only pass the name , the author knows value may be null as default value of String is null, so he can take care of it , So here use of Optional is redundant-- here Optional means Developer of the code reminds himself passing parameter may be present or absent-- just nonsense. Optional works fine for the client of the method as the client does not know about what the method is doing inside he only knows by call findName I can get an Optional<String >, So this method may give a blank result so I need to tackle it. But the reverse perspective is just absurd. There is no need to signal developer as he controls the implementation he knows what to do with inputs, so in this case null check is better than Optional, Another thing is -- by passing an Optional you create a wrapper on a value so it takes more memory space an unnecessary complex the code violation of KISS(keep it simple stupid), Also caller has to create Optional container which is break of encapsulation. So the best way to represent your code is like that
package com.example.optional;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class OptionalTest {
private static List<String>nameList = new ArrayList<String>();
static {
nameList.add("shamik");
nameList.add("samir");
nameList.add("swastika");
}
Optional<String> findName(String name){
return name !=null?Optional.of(nameList.get(nameList.indexOf(name.toLowerCase()))):Optional.empty();
}
public static void main(String[] args) {
OptionalTest optionalTest = new OptionalTest();
Optional<String> searchedNameOptional = optionalTest.findName("Shamik");
Optional<String> searchedNameOptionalSecond = optionalTest.findName(null);
searchedNameOptional.ifPresent(System.out::println);
searchedNameOptionalSecond.ifPresent(System.out::println);
}
}
By this, Developer put the null check, Always remember Optional is not an option for replace null or apply some functional fancy methods apply on value like (filter/map) etc. The Optional is for signaling a value is present or not. So don't use it in composition or input variables for just sake for using Optional.
PO: Now, I understood the same master, Now please tell some of the operation we can do using Optional?
Oogways: Yes PO Now we are in a position where we can use some of the Optional 's utility methods.
orElse and orElseGet: Sometimes, you are sure about what would be the default value if a value is not present in that case you can use orElse on the Optional. Suppose if the name is not found we show "NA" as default value, in that case, we can change the findName method as following
package com.example.optional;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class OptionalTest {
private static List<String>nameList = new ArrayList<String>();
static {
nameList.add("shamik");
nameList.add("samir");
nameList.add("swastika");
}
public String findName(String name){
return Optional.ofNullable(name).map(val->nameList.get(nameList.indexOf(val.toLowerCase()))).orElse("NA");
}
public static void main(String[] args) {
OptionalTest optionalTest = new OptionalTest();
String blankName = optionalTest.findName(null);
String name = optionalTest.findName("Shamik");
System.out.println("Name is :: " + blankName);
System.out.println("Name is :: " + name);
}
}
Here I use a map function which will check the name is in name List or not if it is not found it will return "NA" as in orElse I provide the default value as "NA".
Now, If the default value fetched from Database or from a locale based properties file then we need to write a separate method which returns the default value, in that case, we can use that separate method as a reference and pass a supplier interface in the orElseGet method. See the following example.
isPresent and ifPresent : Optional has two utility functions called isPresent and ifPresent, former returns true if a value present later takes a callback function which will apply on the value if the value is present. So when you see a code block like following
if(optional.isPresent()){
doSomething();
}
replace the same with ifPresent
optional.ifPresent(val->doSomething())
see the below Example,
package com.example.optional;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class OptionalTest {
private static List<String>nameList = new ArrayList<String>();
static {
nameList.add("shamik");
nameList.add("samir");
nameList.add("swastika");
}
Optional<String> findName(Optional<String> name){
return name.isPresent()?Optional.of(nameList.get(nameList.indexOf(name.get().toLowerCase()))):Optional.empty();
}
public static void main(String[] args) {
OptionalTest optionalTest = new OptionalTest();
Optional<String> searchedNameOptional = optionalTest.findName(Optional.of("Shamik"));
if(searchedNameOptional.isPresent()) {
System.out.println(searchedNameOptional.get());
}
searchedNameOptional.ifPresent(System.out::println);
}
}
Here, I replace isPresent with ifPresent.
flatMap: The flatmap function works same as map function, i.e change one data structure to another data structure but if the return data structure holds an Optional it does not create a nested Optional Structure Optional<Optional<T>> it just returns only Optiona<T>
see the example
package com.example.optional;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class OptionalTest {
public void flatMapTest(String name) {
Optional<String> retName = Optional.of(name).flatMap(val->Optional.of(val));
System.out.println(retName);
}
public static void main(String[] args) {
OptionalTest optionalTest = new OptionalTest();
optionalTest.flatMapTest("Shamik");
}
}
in FlatMap fuction I delibaretly return Optional.of(val) and as flatMap returns Otional<T> it should be then Optional<Optional<String>> but it returns only
Optional<String> if we use map function we got Optional<Optional<String>>
filter: we can use filter function on Optional so we can filter the value based on some criteria
package com.example.optional;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class OptionalTest {
private static List<String>nameList = new ArrayList<String>();
static {
nameList.add("shamik");
nameList.add("samir");
nameList.add("swastika");
}
Optional<String> findName(Optional<String> name){
return name.isPresent()?Optional.of(nameList.get(nameList.indexOf(name.get().toLowerCase()))):Optional.empty();
}
public static void main(String[] args) {
OptionalTest optionalTest = new OptionalTest();
Optional<String> searchedNameOptional = optionalTest.findName(Optional.of("Shamik"));
searchedNameOptional.filter(val->val.contains("am")).ifPresent(System.out::println);
}
}
PO: Master I learned all the important functions is anything still left to know about Optional?
OogWays: Yes, PO we have covered 90% but one more thing is still pending why Optional is not Serializable, I will give you the answer but I want you to think about it. Tomorrow I will give the answer if you are not able to answer it.
PO: Ok Master.