What are some of worst
punishments for theft?
- Mutilation of a hand
- Exile
- Hard Labor
- Prison
- Penalty Fee
Could we consider some of those for coders that use irresponsibly null?
Not a new idea...
A Million Ways to Die
(if you use null)
All SuperHeroes must die
(when they use null)
Die Hard
(after using null)
Machete Kills
(using nulls)
Null Must Die
Seriosuly...
2016 should be
the year in which
we stop using null
in our code!
What is null?
If you Google it,
you may find
that there are 9
meanings for null...
...that proves that
coders, above all, have misused null... a lot!
The real
definition of null?
The value for a
reference that doesn't
refer to an object
A way to say:
"this reference doesn't refer to anything"
It's a little confusing, I know!
int main() {
int n;
// possibly more code
n = foo();
}
People used to
code like this!
int main() {
int n = 0;
// possibly more code
n = foo();
}
Later someone thought a "better" way
What about
pointers / references?
What is a good "zero"
value for a reference?!
NULL
I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years. -- C.A.R Hoare
If people only use null
as a "zero" value for reference there
wouldn't be much
of a problem
Lisp
A programming language made on the idea of processing lists. How was them implemented?
A List is
- A node, called head which holds
a value and a reference... - ... to another list, called the tail
- An empty list is called nil, and it
is used to signal the end of the list
When someone thought of implementing that idea in OO...
public class LinkedList<T> {
private class Node {
private T value;
private Node next = null;
public Node(T value) {
this.value = value;
}
}
private Node head = null;
private Node last = null;
public void add(T value) {
Node temp = last;
Node node = new Node(value);
last = node;
if (head == null) {
head = node;
} else {
temp.next = node;
}
}
}
That seems
harmless, right?
null is not the same as nil
- nil is an empty list
- null signals that a node
doesn't have a neighbor, yet - One option for implementing nil
is to use a null reference
What would be a more accurate implementation?
public class LinkedList<T> {
private Node NIL = new Node();
private class Node {
private T value;
private Node next = null;
private boolean isEmpty;
public Node(T value) {
this.value = value;
isEmpty = false;
next = NIL;
}
public Node() {
isEmpty = true;
}
}
private Node head = NIL;
private Node last = NIL;
public void add(T value) {
Node temp = last;
Node node = new Node(value);
last = node;
if (head.isEmpty) {
head = node;
} else {
temp.next = node;
}
}
}
Going back
to null, here's
when it
becomes harmful...
V Map::get(Object key)
Returns the value to which the specified
key is mapped, or null if this map
contains no mapping for the key.
When you jump from
zero value to signal absence of value, you
have a huge problem!
There is nothing in the signature of a method
that says that a null
may be returned!
factory.getFoo().getProperty("bar").getString();
This looks harmless!
...what about nulls?
It's so easy to forget
null-checking!
And null checking code is way messier!
These approaches hide
the real problem!
If you were to
paint a portrait
you wouldn't use
a roller brush!
It's not the right tool!
Null should be used as...
an implementation detail
public class LinkedList<T> {
private Node NIL = new Node();
private class Node {
private T value;
private Node next = null; // I'm null but nobody will ever know :)
private boolean isEmpty;
public Node(T value) {
this.value = value;
isEmpty = false;
next = NIL;
}
public Node() {
isEmpty = true;
}
}
private Node head = NIL;
private Node last = NIL;
public void add(T value) {
Node temp = last;
Node node = new Node(value);
last = node;
if (head.isEmpty) {
head = node;
} else {
temp.next = node;
}
}
}
Never let null be
part of the API
of your code!
Are you a
believer now?
This could be the Programming Community when we all
agree to use null just as an implementation detail!
How do we write null-less code?
Step #1
Don't accept null arguments in methods
void doIt(Foo foo, Bar bar) {
if (bar == null) {
// do this
} else {
// something else
}
}
Your code looks messier, it's probably breaking SRP and is more error prone
doIt(foo, null);
This looks weird
doIt(foo);
Much better
doItWithABar(foo, bar);
void doIt(Foo foo) {
Bar bar = giveMeADefaultBar();
doItWithABar(foo, bar);
}
If you will need a bar anyway
// code
Bar bar = service.getIt();
doIt(foo, bar);
The problem with defensiveness
void doIt(Foo foo, Bar bar) {
if (bar == null) { // y'know because null happens!
// code that doesn't use bar
} else {
// code that use it
}
}
Look Ma, I'm a defensive programmer
Are you sure
null just happens?
Whenever you are defensive ask yourself:
when should that parameter be null.
// code
Bar bar = service.getIt();
doIt(foo, bar);
Are you expecting getIt() to return null?
-
If answer is "no", then you don't need to be defensive!
-
If answer is "yes" and you expect it, then use an if/else to call the method that doesn't use the parameter!
Being defensive doesn't mean that you have to
code blindly
void doIt(Foo foo, Bar bar) {
java.util.Objects.requireNonNull(foo);
java.util.Objects.requireNonNull(bar);
// more code
}
Now that you don't need to be defensive, you still need to validate parameters
Objects is part of the Java API
since 1.7. Can't use that version?
Use Guava Preconditions!
Basz(Foo foo) {
this.foo = Objects.requireNonNull(foo);
// more code
}
You may even use the return value in assignments
Always validate your parameters in the first lines of code, so you fail-fast!
Step #2
Never return null
You don't return
null because the caller
of the method may not realize its getting one
But some methods need a way to signal an absence of a value!
You can do a try-or-else method
String getValueOrElse(Foo foo, String defaultValue) {
// code
String value = ...; // I calculate the vale somehow
return value != null ? value : defaultValue;
}
Java 8 introduced a getOrDefault method in the Map interface
You can return a null-object
List<Foo> getFoos() {
// code
List<Foo> foos = ...; // Get the list of foos
return foos.size > 0 ? foos : Collections.emptyList();
}
A null object is an object which state makes it
clear that the object is
"empty" but that it doesn't
break our code
An empty collection
is a null object for
that collection
Creating null objects is
not as easy as following
a formula, almost
every case is unique
Finally you may opt
for Java's Optional
or Guava's Optional
public void run() throws IOException {
try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(source)) {
for (Path path : directoryStream) {
if (Images.isImage(path)) {
Image image = new Image(path);
if (image.isProportional(width, height)) {
image = image.resize(width, height);
image.write(destination);
}
}
}
}
}
You can go from this...
public void run() throws IOException {
try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(source)) {
for (Path path : directoryStream) {
Optional.of(path)
.filter(Images::isImage)
.map(Image::of)
.filter(image -> image.isProportial(width, height))
.map(image -> image.resize(width, height))
.forEach(Image::write);
}
}
}
... to this.
If one of the methods of Optional returns a null, Optional becomes an empty Optional and subsequent operations
are ignored
Optional<Foo> getFoo() {
// code
}
Method's signature is honest, you know you may not get a foo after all
To avoid returning null
- Use a "wrapper" get-or-else method
- Return a Null-Object
- Return an Optional<T>
There is one more thing... good exception handling
public Foo getFoo(Bar bar) {
Foo foo = null;
try {
foo = bar.something();
} catch (MyException e) {
LOGGER.error();
}
return foo;
}
Have you seen code like this?
Why do you need a null reference?
ByteArrayOutputStream outputStream = null;
// this need to be visible...
GZIPOutputStream gzipOutputStream = null;
try {
outputStream = new ByteArrayOutputStream();
gzipOutputStream = new GZIPOutputStream(outputStream);
gzipOutputStream.write(data);
return outputStream.toByteArray();
catch (Exception e) {
// error handling
} finally {
if (gzipOutputStream != null) {
gzipOutputStream.close(); // ...here!!
}
}
There was only 1
good reason to have null references... resources managment
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
GZIPOutputStream gzipOutputStream =new GZIPOutputStream(outputStream)) {
gzipOutputStream.write(data);
return outputStream.toByteArray();
catch (Exception e) {
// error handling
}
Since Java 7, that's no longer the case
But, bad habits die hard, so people used null references before a try statement even if they don't need them, and IDE's have part of the blame!
void something() {
Foo foo = getFoo(); // this throws an exception
}
As soon as you type this...
your IDE shows a compiler error
void something() {
Foo foo = null;
try {
foo = getFoo(); // this throws an exception
} catch (MyException e) {
}
}
If you choose "surround with try/catch", the results is always
Why?!, Why?!
Don't always trust blindly your IDE or
a coworker!
Your try statement should be your fist line of code
Your catch statement should be the last
(remember, since
Java 7 you don't need finally statements)
void doIt() {
try { // no code before
} catch (MyException e) {
} // no code after
}
Foo getFoo(Bar bar) {
try {
Foo foo = bar.something();
return foo;
} catch (MyException e) {
throw new AssertionError(e);
}
}
It should be like this...
Optional<Foo> getFoo(Bar bar) {
try {
Foo foo = bar.something();
return Optional.of(foo);
} catch (MyException e) {
return Optional.empty();
}
}
or...
void doIt(Bar bar) {
try {
Foo foo = bar.something();
foo.amazeMe();
} catch (MyException e) {
LOGGER.info("This failed");
}
}
or...
Foo getFoo(Bar bar) throws MyException {
Foo foo = bar.something();
return foo;
}
or...
Don't catch an exception blindly! Give some
thought on your error handling strategy.
Instead of returning null
- Propagate the exception
- Throw an AssertionError
- Change the signature to return Optional
Step #3
Never instantiate to null
So we come back to the origin of null
Never introduce
a variable when
you don't have a value for it
But there are some
very common uses of a variable initialized to null
Foo something = null;
if (/* a contional */) {
something = ...;
} else {
// complex code
something = ...;
}
// code that uses something
Can we avoid the null reference?
Culprit #1
Foo something = /* a contional */ ? value 1 : value 2;
// code that uses something
Foo something = /* a contional */ ? value 1 : get();
// code that uses something
Foo something = get();
* * *
Foo get() {
if (/* a conditional */) {
return value 1;
}
// complex code to calculate value
return value 2
}
Avoid null
- Use the tertiary operator
- Use functions
- Use combination of both
public T fullMonty() {
T obj = null;
try {
// code
} catch (ABCException e) {
// code
}
return obj;
}
Culprit #2: The Big One
Can we avoid the null reference?
It's not clear if the intention is to return null or not
Clean Code's recipe
- There is no code before
the try statement - There is no code after
the last catch statement - Have a public method that
only has error handling code - Have a private method that
throws exceptions and have
only logic
public T fullMonty(/* params */) {
try {
return doFullMonty(/* params */);
} catch (ABCException e) {
// error handling code
// here you have visibility
// of all params
}
}
private T doFullMonty(/* params */) throws ABCException {
// simple code
return tValue;
}
public T fullMonty() {
T obj = null;
try {
// code
} catch (ABCException e) {
// code
}
return obj;
}
You can go from this ...
public Optional<T> fullMonty(Bar bar) {
try {
return Optional.of(doFullMonty(bar));
} catch (ABCException e) {
LOGGER.error("Full monty failed on {}", bar);
return Optional.empty();
}
}
to this...
private T doFullMonty(Bar bar) throws ABCException {
// easy to understand code
return tValue;
}
Avoid using null
- foo public method with error handling code
- doFoo private method throws exception has easy to understand code
- no code before try statement and no code before last catch statement
String line = null;
while ((line = br.readLine()) != null) {
// do something with line
}
Culprit #3: Legacy Code
Can we avoid the null reference?
String line = br.readLine();
while (line != null) {
// do something with line
line = br.readLine();
}
If you saw code like this, it doesn't
mean that you have to used as is
Avoid null
- Don't copy/paste bad code, improve it
- Try to use modern libraries
- If using legacy code you end with null in
conditionals that's ok ... - ... if not, create wrapper methods
Let's make a vow!
Because this is not funny!
The best way to eliminate NPE is to get rid of null!
I vow, to the best
of my possibilities
- to relegate null to an implementation detail
- to refactor code that I use to not use null anymore
- to explain to others why those ideas are important
References
Q&A
Thanks!
@gaijinco
gaijinco@gmail.com
KILL NULL
By Carlos Obregón
KILL NULL
Best practices on how to write code that doesn't use null at all
- 2,048