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 
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 
mrcellux wrote:
You jump now to the article and the new example because you can't do so?


Who was the one making the point that being able to delay handling was necessary? So I pointed you to your article which stated this. So "picking apart that one line of code" included the fact that based on YOUR words of what was important, you are not allowing for delayed processing.

That was my issue with your purposefully bias representation of the examples. You went out of your way to make the example supporting your own view as succinct, and then purposely wrote the other example verbosely and grossly bloated just to make a point.

Either delayed processing IS important, or it is not, and if you are going to give examples to illustrate that, then BOTH examples should illustrate that.

I also believe that I did point out how your example is limited in your use of linq. Clearly you have never debugged through a ForEach statement before, or you would understand this, and clearly you have never bothered looking at an exception that is thrown by code inside a ForEach loop either. Your example forces the processing of all items without any capability for handling individual failures.

mrcellux wrote:
1) without ifpresent/get you get compile error. Important difference.


Are you being purposely dense? seriously... Do you honestly think I am talking about removing code which makes it not compile and putting that forward as a valid option? Seriously mate, you come up with some completely insane rebuttals some times. First you argued that Hungarian notation is one of the main reasons that the mechanical behavior of a solution was wrong, NOW you are using inability to compile by making the stupid presumption that what I meant was to just remove code and have it fail.

Is that really your #1 response? A compile error. That is your hands down show stopper of a response?

mrcellux wrote:
2) you being the author is not given. We don't code alone in a cave, we have to communicate through are apis (even with implementations)


And yet another completely useless comment. What on earth does this mean? If you are trying to say that our code needs to explain what it does, I dont see how I wasn't explaining that. Heck, your whole original argument was that null wasn't explicitly indicated in the syntax of the method, which is why I gave you an existing example that does, and I could come up with dozens of others which still don't need your bloated and overweight poor "generic" solution.

mrcellux wrote:
3) the linq code is not needed sure. It just makes things safer, shorter, more declarative


No, you used it specifically to try and make your example look better, which is why you went out of your way to add extra lines of code in the alternate example. Do you really have to go that far out of your way to try and make your solution look good? Shouldn't it just stand on its own two feet?

Seriously, all you needed to do in the beginning was say "sure, if all you need is just a single value then my solution is complete overkill. I stand corrected, my solution isn't for all situations, it is more geared towards those places where you dont have a choice and where you have a need to return more complex data", and I would have been happy. But as I said before, you just doubled down and acted like your solution does everything.

I even handed you a fricken bone and told you that things like event args or background worker request/response classes your solution would be perfect, because in those cases you are FORCED to use only a single object to convey all the information you require... but oh no... dont ever take a step back and correct yourself. Instead, just soldier on, push forward and quote stupid things like "it wont compile if I remove code"

mrcellux wrote:
I didn't say this should be used everywhere


Really? I re-read the article looking for ANYWHERE you actually mentioned where this solution should be used and I couldn't find any. What i did find however were lots of "commonly used", "many languages", "many contexts", "an alternative", "with consistent use".... So I am sorry, but failing to explicitly say something doesn't mean you didn't more than imply it with how common and generic you are claiming it is. Even when given the opportunity to clarify it when I pointed it out, instead of going "oh you are right, it shouldn't be used in most cases" you just doubled down again. So even now that you are making a point of NOT saying it should be used everywhere.... you still dont say that it shouldn't be used everywhere.

mrcellux wrote:
and also show the drawbacks (conclusion section).


It is interesting you should use the word "drawback". It is only mentioned ONCE in the entire article, and that is to describe the problem you are solving.

But here is your disadvantages:

"Disadvantages? In most cases there aren't, but as to everything there are exceptions. The Optional is a heap object, this needs to be considered in extreme cases. There may be specific scenarios where such practice doesn't deliver real value or is impractical"

Wow... "in MOST cases"... but that sounds like the vast majority of cases SHOULD use this. "Extreme Cases", so just wanting an integer is an extreme case? Wanting a simple object is an extreme case?

Honestly, if you are going to write articles, you need to NOT pat yourself on the back so much and slant everything towards your own solution being some holy grail.

mrcellux wrote:
Shoot. Or is it not bad?


I have already been over this a dozen times, and I have even explained it further above, which I have done so many times previously. But what the heck, let me go even deeper into pulling this to pieces.

ifPresent
What an awesome little piece of code. You DO know what the code inside this method is don't you? You do understand the internal compiler directive which performs the is null check and then conditionally executes the next line of code when it exists. So you DO actually realize that anyone can write their own extension function that takes in an expression and performs EXACTLY the same as this. So sorry, you dont get to take credit for other people's coding.

LinQ
You are aware of the overhead of using Linq aren't you? The memory requirements of setting up a functional expression? You are setting up an additional overhead of having to create additional classes for no other reason than to support the Linq interface to make one line of code look pretty. You would achieve this with far less memory and far fewer cycles by simply performing your own null check and then only execute if it exists. Sure it is a few more lines, but doing something PURELY for the code to look good is as I say VERY.POOR.CODING. When it is more important for you to make that line pretty than to think about how the code itself will run and how it will function is bad.

Exception Handling
Said it above, but will repeat it here in case you missed it like all the other times I have spelt out problems. There is no means of handling errors from within Linq statements, you ave not given any indication of where the problem is within the stack trace, and you cannot manage the exception. I noticed that you purposely reduced your example now to leave out the ForEach, which would have caused an even greater problem with handling exceptions. That is VERY.POOR.CODING.


But here is your chance... despite all of this, and you doubling down. Here is your opportunity to explicitly state when this should and shouldn't be used, perhaps even changing the article to reflect this instead of making it sound like there are very few "extreme" cases where it shouldn't be used.

Or are you so protective of your little piece of code that you care more about how it looks than it being correct?
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.