Okay, I've got a question about handling nulls. This question can be heavily based upon opinion, therefore I'm going to ask about pro's and cons.
Let's say I've got a function that can return null, or a JSONArray. I always want a JSONArray, so I want it to create an empty one if the function's result is null.
Currently I've got the following approach:
jsonArray = jsonArray==null?new JSONArray():jsonArray;
I like this approach as it's one line, and pretty clear what it does. This does lead me to the question though, is this efficient? I've got the idea that now it'll execute jsonArray = jsonArray
while not needed. Though this does seem to save one jump you would have with an if (jsonArray == null)
What are the advantages of different ways of handling nulls?
Have you taken a look at Java 8's Optional
class? This is an object wrapper that lets you handle null in a functional way.
For example, if you have a method public JSONArray getArray()
that you want to always return something other than null, you can use your code. Using Optional, it would change to this:
public Optional<JSONArray> getArray() {
// jsonArray comes from somewhere
return Optional.ofNullable(jsonArray);
}
In cases where jsonArray is null, the optional will be empty; in cases where it's not null, it will contain jsonArray.
You can then replace null checks with behaviour dictated by the optional. Instead of
JSONArray array = getArray();
if (array != null) {
// do something
}
you replace it with
getArray().ifPresent(array -> // do something);
This means you don't need to create empty JSONArrays, or Lists, or Sets, or Strings, or whatever. In cases where the wrapped object is actually null, a singleton Optional is returned from Optional.ofNullable
, further reducing overhead.
If you still want to take a classic approach, that's possible too. Since if (option == null)
should always evaluate to false
(if you return null instead of an Optional, you kind of miss the point!), you woud use if (option.isPresent())
.
If you're not using Java 8, you can either write your own Optional or use a third-party library such as Guava.
EDIT: Non-Java 8 solutions
Solution 1
Use something like Guava - take a look at http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/Optional.html
Solution 2
Write your own! In this implementation, Supplier
, Consumer
and Predicate
are interfaces that return, accept or test an object.
public abstract class Option<T> implements Iterable<T> {
private static final Option NONE = new None();
private Option() {
// no-op
}
public static <T> Option<T> of(T t) {
return t == null ? NONE : new Some<T>(t);
}
public static <T> Option<T> empty() {
return NONE;
}
public abstract T get();
public abstract T orElse(T fallback);
public abstract T orElse(Supplier<T> supplier);
public abstract <E extends Exception> T orThrow(Supplier<E> exceptionSupplier) throws E;
public abstract boolean isPresent();
public abstract Option<T> filter(Predicate<T> predicate);
public abstract void ifPresent(Consumer<T> consumer);
public abstract <O> Option<O> ifPresent(Function<T, O> function);
private static final class Some<T> extends Option<T> {
private final T value;
private Some(final T value) {
this.value = value;
}
@Override
public T get() {
return value;
}
@Override
public T orElse(final T fallback) {
return value;
}
@Override
public T orElse(final Supplier<T> supplier) {
return value;
}
@Override
public <E extends Exception> T orThrow(final Supplier<E> exceptionSupplier) throws E {
return value;
}
@Override
public boolean isPresent() {
return true;
}
@Override
public Option<T> filter(final Predicate<T> predicate) {
return predicate.test(value) ? this
: NONE;
}
@Override
public void ifPresent(final Consumer<T> consumer) {
consumer.consume(value);
}
@Override
public <O> Option<O> ifPresent(final Function<T, O> function) {
return Option.of(function.apply(value));
}
@Override
public Iterator<T> iterator() {
return Collections.singletonList(value).iterator();
}
}
private static final class None<T> extends Option<T> {
@Override
public T get() {
throw new IllegalStateException("value not defined");
}
@Override
public T orElse(final T fallback) {
return fallback;
}
@Override
public T orElse(final Supplier<T> supplier) {
return supplier.get();
}
@Override
public <E extends Exception> T orThrow(final Supplier<E> exceptionSupplier) throws E {
throw exceptionSupplier.get();
}
@Override
public boolean isPresent() {
return false;
}
@Override
public Option<T> filter(final Predicate<T> predicate) {
return this;
}
@Override
public void ifPresent(final Consumer<T> consumer) {
// no-op
}
@Override
public <O> Option<O> ifPresent(final Function<T, O> function) {
return NONE;
}
@Override
public Iterator<T> iterator() {
return Collections.<T>emptyList().iterator();
}
}
}