Click here to Skip to main content
15,887,027 members
Articles / Programming Languages / Java

Null is Optional

Rate me:
Please Sign up or sign in to vote.
4.33/5 (39 votes)
13 Dec 2015CPOL3 min read 65.7K   9   47
Null is optional

Introduction

Null reference is commonly used to express the absence of value. It has a certain drawback though in most languages (more specifically in statically typed ones allowing nullable references without explicit syntax). Let's take a look at the following method:

Java
User findUser(String name);

What happens when that user can't be found? Returning null expresses the concept of an absent value in probably the simplest way, but it has a specific drawback: the possibility of the null reference is implicit, the interface doesn't reveal this. Why is that a big deal? Null references need handling, otherwise NullPointerException may occur at runtime, a very common error:

Java
findUser("jonhdoe").address(); // NullPointerException at runtime

Generally not all the values are nullable in a system. Indicating where exactly handling is needed is useful to reduce both risks and unnecessary effort. Documentation or meta code can communicate nullability, but they're not ideal: they are possible to miss and lack compile safety.

Some languages don't allow nullable references by default and have special syntax for allowing them. Most languages can't do this - in most cases for compatibility reasons - and have an alternative to address the issue by making use of their type type system: the option type. It's useful to know about this pattern as it can be easily implemented in most languages - if it's not implemented already. Let's take a look at Java 8's Optional for instance. "A container object which may or may not contain a non-null value."

Java
Optional<User> findUser(String name);

This interface communicates clearly that a user may not be present in the result and the client is forced to take that into account:

Java
// findUser("jonhdoe").address(); compile error!

Handling the absent value:

Java
Optional<User> user = findUser("johndoe");
if(user.isPresent()) {
  user.get().address();
}

This example is intentionally kept somewhat similar to how null references are often handled. A more idiomatic way of doing the same:

Java
findUser("johndoe").ifPresent(user -> user.address());

It's interesting to consider the effects of the pattern in the wider context of a system. With consistent use of Optional, it is possible to establish a powerful convention of avoiding the use of null references. It transforms an interface from:

Java
interface User {
  String name();
  Address address();
  BankAccount account();
}

to:

Java
interface User {
  String name();
  Address address();
  Optional<BankAccount> account();
}

The client can see a user may not have a bank account and can assume it always has a name and address (the convention at play here). The domain model became more expressive. Such practice works well with changes: eg if address becomes optional at some point in the future all client code will be forced to conform. The code examples so far presented the effects on method signatures, the same benefits apply to class fields or local variables:

Java
class Address {
  String city;
  Optional<String> street;
}

As a small bonus, there is some more syntax sugar that simplifies code in a lot of scenarios:

Java
Optional<String> foo = Optional.ofNullable(thirdPartyApiThatMayReturnNull());
String foo2 = foo.orElse("No value.. take this default one.");
String foo3 = foo.orElseThrow(() -> new RuntimeException("I really can't work without a value though"));
thirdPartyApiExpectingNull(foo.orElse(null));
if(foo.equals(foo2)) { // no NullPointerException, even if foo is absent
  // ...
}

Conclusion

In most cases, avoiding implicit nulls as much as possible can do wonders to a codebase, makes it a much safer place to be. Disadvantages? Patterns like the option type work well in most cases, but as with everything there are exceptions: it's a heap object, this needs to be considered if the number of objects is extremely high. There may be specific scenarios where such practice doesn't deliver real value or is impractical. 3rd party code may also force the use of null references.

An example of the use of optional: the java 8's Stream API

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Norway Norway
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionI know this is an old post Pin
Sacha Barber2-Feb-18 1:49
Sacha Barber2-Feb-18 1:49 
GeneralMy vote of 5 Pin
dmjm-h8-Dec-15 9:50
dmjm-h8-Dec-15 9:50 
QuestionWhats so bad about null checking? Pin
GerVenson8-Dec-15 2:11
professionalGerVenson8-Dec-15 2:11 
AnswerRe: Whats so bad about null checking? Pin
mrcellux8-Dec-15 4:09
mrcellux8-Dec-15 4:09 
Question[My vote of 1] Dummy article Pin
Thornik28-Nov-15 12:47
Thornik28-Nov-15 12:47 
AnswerRe: [My vote of 1] Dummy article Pin
mrcellux28-Nov-15 21:30
mrcellux28-Nov-15 21:30 
GeneralRe: [My vote of 1] Dummy article Pin
Eddy Vluggen31-Jan-18 14:35
professionalEddy Vluggen31-Jan-18 14:35 
PraiseWhy I like this Pin
MuThink27-Nov-15 1:49
MuThink27-Nov-15 1:49 
GeneralRe: Why I like this Pin
Eddy Vluggen31-Jan-18 14:37
professionalEddy Vluggen31-Jan-18 14:37 
QuestionIsn't this EXACTLY what TryParse is for? Pin
Enigmaticatious23-Nov-15 15:11
Enigmaticatious23-Nov-15 15:11 
AnswerRe: Isn't this EXACTLY what TryParse is for? Pin
mrcellux23-Nov-15 21:53
mrcellux23-Nov-15 21:53 
GeneralRe: Isn't this EXACTLY what TryParse is for? Pin
Enigmaticatious24-Nov-15 13:48
Enigmaticatious24-Nov-15 13:48 
GeneralRe: Isn't this EXACTLY what TryParse is for? Pin
mrcellux24-Nov-15 18:27
mrcellux24-Nov-15 18:27 
GeneralRe: Isn't this EXACTLY what TryParse is for? Pin
Enigmaticatious25-Nov-15 17:16
Enigmaticatious25-Nov-15 17:16 
GeneralRe: Isn't this EXACTLY what TryParse is for? Pin
mrcellux25-Nov-15 20:20
mrcellux25-Nov-15 20:20 
GeneralRe: Isn't this EXACTLY what TryParse is for? Pin
Enigmaticatious26-Nov-15 10:40
Enigmaticatious26-Nov-15 10:40 
GeneralRe: Isn't this EXACTLY what TryParse is for? Pin
Jay Marte25-Nov-15 21:50
Jay Marte25-Nov-15 21:50 
GeneralRe: Isn't this EXACTLY what TryParse is for? Pin
Enigmaticatious26-Nov-15 10:51
Enigmaticatious26-Nov-15 10:51 
GeneralRe: Isn't this EXACTLY what TryParse is for? Pin
mrcellux26-Nov-15 12:00
mrcellux26-Nov-15 12:00 
GeneralRe: Isn't this EXACTLY what TryParse is for? Pin
Enigmaticatious26-Nov-15 13:49
Enigmaticatious26-Nov-15 13:49 
1. "Null check is not performed (hasn't been considered or forgotten)"

Which you then immediately follow with "if(user.isPresent())", which by your own admission could not be performed because it wasn't considered or forgotten.

2. "Time is invested in making sure that implementation never returns a null reference.. Then hope for the best that won't change in the future"

Given that you are the creator of the method, and returning a null is valid, why would you have to invest time in making sure it never returns null? Why would you have to "hope for the best" it doesn't change in the future? How is that any different from someone "hoping for the best" that you dont completely change your structure for Optional<> in the future or change the method calls on it?

3. "Null check is done speculatively, even if it's not needed"

But its ok to use LinQ to perform .Select and .Where "even if its not needed"?


So from your original 3 reasons as to why your solution is better than existing solutions, you have either done the very thing you are saying is wrong with existing solutions, or you have simply replaced one "the developer may write bad code" with a different "the developer may write bad code".

I dont know how many times I have to keep saying this. ALL I did with YOUR example was to try and remove YOUR bias, by removing the superfluous code that you added on purpose to try and make your example look better.


mrcellux wrote:
This is why its difficult to delay handling


He says while providing an example that FORCES you to handle it immediately, with absolutely no provision to delay the handling.

Well done!

The only reason I "get angry" as you put it is the sheer hypocrisy and contradiction where you seem to set up these fictitious reasons why something is bad and then violate them yourself in the very next sentence. If "delaying handling" is something you decide is valuable, then why are all your examples completely ignoring the ability to do this? Why are you making a point of showing verbose examples as being bad when the only way to delay handling is to do the very thing you say is bad?

And as I also keep saying, your "powerful stuff" is completely and utterly bloated overkill if all I want to do is parse a simple value. You keep harping on about immutability and claiming I am "violating" it, yet I explained to you that immutability serves a purpose for a reason. If I were to use Entity framework I CANNOT make my data models immutable, it requires them to be changeable in order to load the classes. Does that automatically mean it is "violating the principle" and is therefore bad?

YOU have decided that immutability is a show stopper in ALL circumstances, but look at your very own example. The only think "immutable" about it is that I cannot change the "isPresent" from being true to false (which nobody would ever want to do) and I cannot change the "user" that it relates to (which again I nobody would ever want to do). So immutability is a completely moot point in your example. The only way in which a lack of immutability would be a problem here is if people are purposely writing malicious code.

Are you always so paranoid about your own developers?

"Statistically", you only need about half the code when you write exactly what you want in exactly the way you want for exactly the purpose you want it. So by all means, write an article stating that here is a solution for one very specific problem, and I would totally agree with you (as I said before, event args and background worker parameters/responses is a good example)... but claim that this is something that should be used "anywhere that can return a null" and I will have to disagree because you violate your own premises with very poor convoluted and arbitrary "problems" you manufacture yourself and then solve with your own solution.
GeneralRe: Isn't this EXACTLY what TryParse is for? Pin
mrcellux26-Nov-15 18:16
mrcellux26-Nov-15 18:16 
GeneralRe: Isn't this EXACTLY what TryParse is for? Pin
Enigmaticatious29-Nov-15 10:57
Enigmaticatious29-Nov-15 10:57 
GeneralRe: Isn't this EXACTLY what TryParse is for? Pin
mrcellux26-Nov-15 12:16
mrcellux26-Nov-15 12:16 
GeneralRe: Isn't this EXACTLY what TryParse is for? Pin
Jay Marte26-Nov-15 22:02
Jay Marte26-Nov-15 22:02 
GeneralRe: Isn't this EXACTLY what TryParse is for? Pin
Enigmaticatious29-Nov-15 11:03
Enigmaticatious29-Nov-15 11:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.