Using Optional class to solve NullPointerException

Keywords: Java

On null pointer exception

In the development process, we inevitably encounter a NullPointerException from Java, such as a simple example.

public class NullDemo{
	public static void main(String[] args) {
		MessageUtil.useMessage(MessageUtil.getMessage());
	}
}

interface IMessage{
	public String getContent() ;
}

class MessageImpl implements IMessage{

	@Override
	public String getContent() {
		return "www.baidu.com" ;
	}
	
}

class MessageUtil{
	private MessageUtil(){}
	public static IMessage getMessage(){
		IMessage msg = new MessageImpl() ;
		return msg ;
	}
	public static void useMessage(IMessage msg){
		System.out.println(msg.getContent());
	}
}

In the above code, we use the interface implementation class MessageImpl to obtain information, use a factory design pattern method to obtain the instantiated object of the interface implementation class in the tool class, and use another method to obtain information from the implementation class.
The factory production instantiation object is used in the main method, and the object is passed in as a parameter to the method obtaining information.

Problem elicitation

The above code looks perfect, but it's just a simple example. In the actual development, we have encountered more complex situations, such as the getMessage() method. If we can't get the instantiated object, what happens if we return a Null?

class MessageUtil{
	private MessageUtil(){}
	public static IMessage getMessage(){
		return null ;
	}
	public static void useMessage(IMessage msg){
		System.out.println(msg.getContent());
	}
}

Exception in thread "main" java.lang.NullPointerException
at dataStructure.MessageUtil.useMessage(NullDemo.java:30)
at dataStructure.NullDemo.main(NullDemo.java:7)

Let's look at the exception information. We can see that although the problem occurs in getMessage(), it returns null, but the error is actually the useMessage() method. Is there any way to solve this?

class MessageUtil{
	private MessageUtil(){}
	public static IMessage getMessage(){
		return null ;
	}
	public static void useMessage(IMessage msg){
		if(msg != null)
			System.out.println(msg.getContent());
	}
}

Traditionally, we make a judgment on whether a parameter is null when executing a method. If so, we directly skip the execution and run the code without any error. But it's not good. It's like we go to the bank to get money. The staff of the bank is not responsible for the truth of the money given, but for our customers to judge by themselves. In a large project, if every method judges, the code is too redundant, so we should kill the problem in the cradle
The cradle here is our factory getMessage() method. If we can ensure that it always gives the correct instantiation object, we don't have many troubles.

Export Optional class

Fortunately, java provides a method to deal with empty objects in 1.8. Let's take a look at a piece of code

class MessageUtil{
	private MessageUtil(){}
	public static Optional<IMessage> getMessage(){
		return Optional.of(new MessageImpl()) ;
	}
	public static void useMessage(IMessage msg){
		if(msg != null)
			System.out.println(msg.getContent());
	}
}

Exception in thread "main" java.lang.NullPointerException
at java.util.Objects.requireNonNull(Objects.java:203)
at java.util.Optional.(Optional.java:96)
at java.util.Optional.of(Optional.java:108)
at dataStructure.MessageUtil.getMessage(NullDemo.java:27)
at dataStructure.NullDemo.main(NullDemo.java:7)

In the production instantiation process of this code, we add a Optional.of() judge. At this time, we pass in an empty object. You can see that in the console, the java error reporting method has changed from useMessage() to getMessage(). Back to the bank example, we can say that the work of judging counterfeit currency has been transferred from the customer to the bank employee.

Other features of Optional

Of course, Optional can not only judge empty objects in production, but also process them. Let's look at a piece of code

public class NullDemo{
	public static void main(String[] args) {
		MessageUtil.useMessage(MessageUtil.getMessage().orElse(new MessageImpl()));
	}
}

interface IMessage{
	public String getContent() ;
}

class MessageImpl implements IMessage{

	@Override
	public String getContent() {
		return "www.baidu.com" ;
	}
	
}

class MessageUtil{
	private MessageUtil(){}
	public static Optional<IMessage> getMessage(){
		return Optional.ofNullable(null) ;
	}
	public static void useMessage(IMessage msg){
		if(msg != null)
			System.out.println(msg.getContent());
	}
}

In this code, we change the statement to judge the empty object into Optional.ofNullable() method, which means that we can pass in empty objects. In the main method, we use the orElse() method to receive parameters, which means that if we instantiate objects, we will use them directly, and if we receive empty objects, we will produce instantiated objects ourselves.
Then we will finish the Optional class here!

Posted by bigdaddysheikh on Mon, 08 Jun 2020 18:18:48 -0700