Saturday, 15 August 2015

Java8: Optional class: Working with NullPointerException

If you are a java developer, many times you may fed up with handling NullPointer exceptions. Usually NullPointerException stops your program executing further, unless you handle them properly. Java8 solved NullPointerException problem by providing java.util.Optional<T> class. Optional class encapsulates an optional value, used to represent null with
absent value.

What is NullPointerException?
NullPointerException is a runtime exception, thrown when an application attempts to use an object reference, having the null value.

Lets take a simple example, how we are dealing with NullPointerExceptions prior to Java8.


Lets say I had three classes Employee, Address, Passport.
class Employee{
 private String firstName;
 private String lastName;
 private int age;
 private Passport passport;
 private Address tempAddress;
 private Address permAddress;
}

class Address {
 private String street;
 private String city;
 private String state;
 private String country;
 private String pin;
}

class Passport {
 private String PassportNum;
 private Date validFrom;
 private Date validTo;
}

Employee must has firstName, id and age
Employee may has lastName (Not necessary)
Employee may have permanent address (Not necessary)
Employee may have temporary address (Not necessary)
Employee may have passport (Not necessary).


So while printing details information about an employee, we need to check null condition for all optional properties like lastName, tempAddress, permAddress, passport like following.
public String getEmployeeDetails(Employee employee) {
 if (employee == null) {
  return "unknown";
 }

 StringBuilder builder = new StringBuilder();

 String lastName = employee.getLastName();
 if (lastName == null) {
  lastName = "unknown";
 }

 Passport passport = employee.getPassport();
 if (passport == null) {
  passport = new Passport("", null, null);
 }

 Address permAddress = employee.getPermAddress();
 if (permAddress == null) {
  permAddress = new Address();
 }

 Address tempAddress = employee.getTempAddress();
 if (tempAddress == null) {
  tempAddress = new Address();
 }

 return builder.append(employee.getFirstName()).append(lastName).append(employee.getId()).append(employee.getAge()).append(passport).append(permAddress).append(tempAddress).toString();

}


Problems with above approach
1.   Don’t you think checking for null conditions for most of the attributes increase code complexity?
2.   If you miss to check null condition for any property like tempAddress, again you end up in NullPointerException.

Using Optional class can solve this problem. Following is the complete application for above example. Next I am going to explain Optional class in detail and rewrite the same example using Optional class.

import java.util.Date;

public class Passport {
 private String PassportNum;
 private Date validFrom;
 private Date validTo;

 Passport() {
  this("unknown", null, null);
 }

 public Passport(String passportNum, Date validFrom, Date validTo) {
  super();
  PassportNum = passportNum;
  this.validFrom = validFrom;
  this.validTo = validTo;
 }

 public String getPassportNum() {
  return PassportNum;
 }

 public void setPassportNum(String passportNum) {
  PassportNum = passportNum;
 }

 public Date getValidFrom() {
  return validFrom;
 }

 public void setValidFrom(Date validFrom) {
  this.validFrom = validFrom;
 }

 public Date getValidTo() {
  return validTo;
 }

 public void setValidTo(Date validTo) {
  this.validTo = validTo;
 }

 @Override
 public String toString() {
  StringBuilder builder = new StringBuilder();
  builder.append("Passport [PassportNum=").append(PassportNum)
    .append(", validFrom=").append(validFrom).append(", validTo=")
    .append(validTo).append("]");
  return builder.toString();
 }

}

public class Address {
 private String street;
 private String city;
 private String state;
 private String country;
 private String pin;

 private static final String DEFAULT = "unknown";

 public Address() {
  this(DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT);
 }

 public Address(String street, String city, String state, String country,
   String pin) {
  super();
  this.street = street;
  this.city = city;
  this.state = state;
  this.country = country;
  this.pin = pin;
 }

 public String getStreet() {
  return street;
 }

 public void setStreet(String street) {
  this.street = street;
 }

 public String getCity() {
  return city;
 }

 public void setCity(String city) {
  this.city = city;
 }

 public String getState() {
  return state;
 }

 public void setState(String state) {
  this.state = state;
 }

 public String getCountry() {
  return country;
 }

 public void setCountry(String country) {
  this.country = country;
 }

 public String getPin() {
  return pin;
 }

 public void setPin(String pin) {
  this.pin = pin;
 }

 @Override
 public String toString() {
  StringBuilder builder = new StringBuilder();
  builder.append("Address [street=").append(street).append(", city=")
    .append(city).append(", state=").append(state)
    .append(", country=").append(country).append(", pin=")
    .append(pin).append("]");
  return builder.toString();
 }

}

public class Employee {
 private String firstName;
 private String lastName;
 private int age;
 private Passport passport;
 private Address tempAddress;
 private Address permAddress;
 private int id;

 public String getFirstName() {
  return firstName;
 }

 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }

 public String getLastName() {
  return lastName;
 }

 public void setLastName(String lastName) {
  this.lastName = lastName;
 }

 public int getAge() {
  return age;
 }

 public void setAge(int age) {
  this.age = age;
 }

 public Passport getPassport() {
  return passport;
 }

 public void setPassport(Passport passport) {
  this.passport = passport;
 }

 public Address getTempAddress() {
  return tempAddress;
 }

 public void setTempAddress(Address tempAddress) {
  this.tempAddress = tempAddress;
 }

 public Address getPermAddress() {
  return permAddress;
 }

 public void setPermAddress(Address permAddress) {
  this.permAddress = permAddress;
 }

 
 public int getId() {
  return id;
 }

 public void setId(int id) {
  this.id = id;
 }

 @Override
 public String toString() {
  StringBuilder builder = new StringBuilder();
  builder.append("Employee [firstName=").append(firstName)
    .append(", lastName=").append(lastName).append(", age=")
    .append(age).append(", passport=").append(passport)
    .append(", tempAddress=").append(tempAddress)
    .append(", permAddress=").append(permAddress).append("]");
  return builder.toString();
 }

}

public class EmployeeUtil {
 public static String getEmployeeDetails(Employee employee) {
  if (employee == null) {
   return "unknown";
  }

  StringBuilder builder = new StringBuilder();

  String lastName = employee.getLastName();
  if (lastName == null) {
   lastName = "unknown";
  }

  Passport passport = employee.getPassport();
  if (passport == null) {
   passport = new Passport("", null, null);
  }

  Address permAddress = employee.getPermAddress();
  if (permAddress == null) {
   permAddress = new Address();
  }

  Address tempAddress = employee.getTempAddress();
  if (tempAddress == null) {
   tempAddress = new Address();
  }

  return builder.append(employee.getFirstName()).append(lastName).append(employee.getId())
    .append(employee.getAge()).append(passport).append(permAddress)
    .append(tempAddress).toString();

 }
 
 public static void main(String args[]){
  Employee employee = new Employee();
  employee.setFirstName("Hari Krishna");
  employee.setAge(26);
  
  String empDetails = getEmployeeDetails(employee);
  System.out.println(empDetails);
 }
}


Optional class
Optional class provides number of methods like ifPresent, orElse, orElseGet to work with null values.

How to create an Optional object?
Following statement creates an Address object.
Optional<Address> addr = Optional.of(new Address());

How to create empty object?
Following statement creates empty Optional object.
Optional<Address> sc = Optional.empty();

Following are all the methods of Optional class in brief
Method
Description
public static <T> Optional<T> empty()
Returns an empty optional object.
public static <T> Optional<T> of(T value)
Returns an Optional with the specified present non-null value. Throws NullpointerException, if value is null
public static <T> Optional<T> ofNullable(T value)
Returns an Optional describing the specified value, if non-null, otherwise returns an empty Optional.
public T get()
If a value is present in this Optional, returns the value, otherwise throws NoSuchElementException.
public boolean isPresent()
Return true if there is a value present, otherwise false.
public void ifPresent(Consumer<? super T> consumer)
If a value is present, invoke the specified consumer with the value, otherwise do nothing.
public Optional<T> filter(Predicate<? super T> predicate)
If a value is present, and the value matches the given predicate, return an Optional describing the value, otherwise return an empty Optional.
public <U> Optional<U> map(Function<? super T,? extends U> mapper)
If a value is present, apply the provided mapping function to it, and if the result is non-null, return an Optional describing the result. Otherwise return an empty Optional.
public T orElse(T other)
Return the value if present, otherwise return other.
public T orElseGet(Supplier<? extends T> other)
Return the value if present, otherwise invoke other and return the result of that invocation.
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier)throws X extends Throwable
Return the contained value, if present, otherwise throw an exception to be created by the provided supplier.

‘getEmployeeDetails’ method as explained above can be rewritten using Optional class like following.

public static String getEmployeeDetailsUsingOptional(Employee employee) {
  StringBuilder builder = new StringBuilder();
  return builder.append(employee.getFirstName())
    .append(employee.getLastName().orElse("unknown"))
    .append(employee.getId()).append(employee.getAge())
    .append(employee.getPassport().orElse(new Passport()))
    .append(employee.getPermAddress().orElse(new Address()))
    .append(employee.getTempAddress().orElse(new Address()))
    .toString();
 }


Find complete working application below.

import java.util.Optional;

public class Employee {
 private String firstName;
 private Optional<String> lastName = Optional.empty();
 private int age;
 private Optional<Passport> passport = Optional.empty();
 private Optional<Address> tempAddress = Optional.empty();
 private Optional<Address> permAddress = Optional.empty();
 private int id;

 public String getFirstName() {
  return firstName;
 }

 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }

 public Optional<String> getLastName() {
  return lastName;
 }

 public void setLastName(Optional<String> lastName) {
  this.lastName = lastName;
 }

 public int getAge() {
  return age;
 }

 public void setAge(int age) {
  this.age = age;
 }

 public Optional<Passport> getPassport() {
  return passport;
 }

 public void setPassport(Optional<Passport> passport) {
  this.passport = passport;
 }

 public Optional<Address> getTempAddress() {
  return tempAddress;
 }

 public void setTempAddress(Optional<Address> tempAddress) {
  this.tempAddress = tempAddress;
 }

 public Optional<Address> getPermAddress() {
  return permAddress;
 }

 public void setPermAddress(Optional<Address> permAddress) {
  this.permAddress = permAddress;
 }

 public int getId() {
  return id;
 }

 public void setId(int id) {
  this.id = id;
 }

 @Override
 public String toString() {
  StringBuilder builder = new StringBuilder();
  builder.append("Employee [firstName=").append(firstName)
    .append(", lastName=").append(lastName).append(", age=")
    .append(age).append(", passport=").append(passport)
    .append(", tempAddress=").append(tempAddress)
    .append(", permAddress=").append(permAddress).append(", id=")
    .append(id).append("]");
  return builder.toString();
 }

}

import java.util.Date;
import java.util.Optional;

public class EmployeeUtil {

 public static String getEmployeeDetailsUsingOptional(Employee employee) {
  StringBuilder builder = new StringBuilder();
  return builder.append(employee.getFirstName())
    .append(employee.getLastName().orElse("unknown"))
    .append(employee.getId()).append(employee.getAge())
    .append(employee.getPassport().orElse(new Passport()))
    .append(employee.getPermAddress().orElse(new Address()))
    .append(employee.getTempAddress().orElse(new Address()))
    .toString();
 }

 public static void main(String args[]) {
  Employee employee = new Employee();
  employee.setFirstName("Hari Krishna");
  employee.setAge(26);
  employee.setPassport(Optional.of(new Passport("abcdf12345", new Date(), new Date())));
  String empDetails = getEmployeeDetailsUsingOptional(employee);
  System.out.println(empDetails);
 }
}


Output

Hari Krishnaunknown026Passport [PassportNum=abcdf12345, validFrom=Wed Jul 22 12:41:40 IST 2015, validTo=Wed Jul 22 12:41:40 IST 2015]Address [street=unknown, city=unknown, state=unknown, country=unknown, pin=unknown]Address [street=unknown, city=unknown, state=unknown, country=unknown, pin=unknown]


Prevoius                                                 Next                                                 Home

No comments:

Post a Comment