68

Let's say I'd like to perform the following command:

house.getFloor(0).getWall(WEST).getDoor().getDoorknob();

To avoid a NullPointerException, I'd have to do the following if:

if (house != null && house.getFloor(0) && house.getFloor(0).getWall(WEST) != null
  && house.getFloor(0).getWall(WEST).getDoor() != null) ...

Is there a way or an already existing Utils class that does this more elegantly, let's say something like the following?

checkForNull(house.getFloor(0).getWall(WEST).getDoor().getDoorknob());
Hubert Kario
  • 19,585
  • 3
  • 22
  • 41
  • 6
    If only you followed the law of Demeter. – Oded Aug 11 '10 at 12:59
  • 24
    Just because I started working on an existing project, I can't redesign it according to my or any Greek deity's law. –  Aug 11 '10 at 13:06
  • We can use Java8 Functional Interface approach. Check this answer: https://stackoverflow.com/a/45319845/822314 – Prathab K Jul 26 '17 at 07:29
  • For anyone else wondering: "In particular, an object should avoid invoking methods of an object returned by another method. ...the law can be stated simply as 'use only one dot'" https://en.wikipedia.org/wiki/Law_of_Demeter – James Daily Nov 12 '21 at 13:59

10 Answers10

106

In case you can't avoid breaking Law of Demeter (LoD) as stated in the chosen answer, and with Java 8 introducing Optional, it would be probably the best practice to handle nulls in chains of gets such as yours.

The Optional type will enable you to pipe multiple map operations (which contain get calls) in a row. Null checks are automatically handled under the hood.

For example, when the objects aren't initialized, no print() will be made and no Exceptions will be thrown. It all we be handled gently under the hood. When objects are initialized, a print will be made.

System.out.println("----- Not Initialized! -----");

Optional.ofNullable(new Outer())
        .map(out -> out.getNested())
        .map(nest -> nest.getInner())
        .map(in -> in.getFoo())
        .ifPresent(foo -> System.out.println("foo: " + foo)); //no print

System.out.println("----- Let's Initialize! -----");

Optional.ofNullable(new OuterInit())
        .map(out -> out.getNestedInit())
        .map(nest -> nest.getInnerInit())
        .map(in -> in.getFoo())
        .ifPresent(foo -> System.out.println("foo: " + foo)); //will print!

class Outer {
    Nested nested;
    Nested getNested() {
        return nested;
    }
}
class Nested {
    Inner inner;
    Inner getInner() {
        return inner;
    }
}
class Inner {
    String foo = "yeah!";
    String getFoo() {
        return foo;
    }
}

class OuterInit {
    NestedInit nested = new NestedInit();
    NestedInit getNestedInit() {
        return nested;
    }
}
class NestedInit {
    InnerInit inner = new InnerInit();
    InnerInit getInnerInit() {
        return inner;
    }
}
class InnerInit {
    String foo = "yeah!";
    String getFoo() {
        return foo;
    }
}

So, with your getters chain it will look like this:

Optional.ofNullable(house)
        .map(house -> house.getFloor(0))
        .map(floorZero -> floorZero.getWall(WEST))
        .map(wallWest -> wallWest.getDoor())
        .map(door -> wallWest.getDoor())

The return of it will be something like Optional<Door> which will allow you much safer work without worrying of null exceptions.

Simeon Leyzerzon
  • 18,180
  • 8
  • 50
  • 72
Johnny
  • 12,661
  • 14
  • 70
  • 112
  • 2
    Neat trick! like a builder of optionals, any link missing in the chain will cause the chain to stop and make the Optional contain null. – nom-mon-ir Jul 08 '19 at 01:29
  • 3
    This should be the correct answer, to be honest, and not the current one! Thanks for the great explanation. – Ahmed Hamdy Feb 18 '21 at 18:43
21

In order to check a chain of gets for null you may need to call your code from a closure. The closure call code will look like this:

public static <T> T opt(Supplier<T> statement) {       
    try {
        return statement.get();
    } catch (NullPointerException exc) {
        return null;
    }   
}

And you call it using the following syntax:

Doorknob knob = opt(() -> house.getFloor(0).getWall(WEST).getDoor().getDoorknob());

This code is also type safe and in general works as intended:

  1. Returns an actual value of the specified type if all the objects in the chain are not null.
  2. Returns null if any of the objects in the chain are null.

You may place opt method into shared util class and use it everywhere in your application.

Simeon Leyzerzon
  • 18,180
  • 8
  • 50
  • 72
Roman Seleznov
  • 394
  • 3
  • 6
  • 5
    No doubt that it is a really tricky way to handle this case but it is a very bad practice to handle null pointer exception as you might end up handling something unintended. You can read the relevant parts of effective java to get a better understanding. – MD. Sahib Bin Mahboob Jul 09 '20 at 08:35
  • 4
    Isn't NullpointerException expensive than a null check ? – user3044440 Dec 11 '20 at 21:21
15

The best way would be to avoid the chain. If you aren't familiar with the Law of Demeter (LoD), in my opinion you should. You've given a perfect example of a message chain that is overly intimate with classes that it has no business knowing anything about.

Law of Demeter: http://en.wikipedia.org/wiki/Law_of_Demeter

Jerod Houghtelling
  • 4,589
  • 1
  • 20
  • 30
  • 34
    This answer gives no guidance on how to avoid the chain, and assumes that OP has the time/permission to redesign the existing code. – K-- Oct 05 '20 at 11:29
8

You could of course simply wrap the whole expression up in a try-catch block, but that's a bad idea. Something cleaner is the Null object pattern. With that, if your house doesn't have floor 0, it just returns a Floor that acts like a regular Floor, but has no real content; Floors, when asked for Walls they don't have, return similar "Null" Walls, etc, down the line.

Jacob van Lingen
  • 8,337
  • 7
  • 43
  • 74
Carl Manaster
  • 39,054
  • 15
  • 99
  • 149
  • 1
    But if a wall doesn't have a door, then returning null is the logical thing to do. Otherwise you will require methods like hasDoor() to know that there is no actual door, since you are just going to get a fake one when you ask for it. – Robin Aug 11 '10 at 13:23
  • @Robin, it's not *exactly* a "fake" wall. It's the wall that isn't, that doesn't exist. And (unlike null) it behaves like a real wall, so it's useful in ways that null can't be. – Carl Manaster Aug 11 '10 at 13:35
  • Using the Null Object pattern is a good option if you don't care whether in the end something will happen or not (+1 for that) – Bozho Aug 11 '10 at 13:41
  • 5
    So basically, you convert a "fail fast" NPE into a "maybe fail at an unspecified later time and place"? There are some cases where null objects make sense (empty collections, most prominently), but IMO they're totally unfit as a general replacement for null. – Michael Borgwardt Aug 11 '10 at 13:57
  • No, I expect there to be a wall, but not necessarily a door (to get picky). I was describing the problem with this approach in the particular example since not all walls will contain doors and the ability to determine if there is a door is made more complicated with the Null Object pattern. @Michael Borgwardt describes the issue in the general sense in his comment. My experience has been that this pattern is rather limited in the applications to which it can be applied. – Robin Aug 11 '10 at 14:02
5

Make sure things that can't logically be null are not. For example - a house always has a West wall. In order to avoid such exceptions in state, you can have methods to check whether the state you expect is present:

if (wall.hasDoor()) {
   wall.getDoor().etc();
}

This is essentially a null-check, but might not always be.

The point is that you should do something in case you have a null. For example - return or throw an IllegalStateException

And what you shouldn't do - don't catch NullPointerException. Runtime exceptions are not for catching - it is not expected that you can recover from them, nor it is a good practice to rely on exceptions for the logic flow. Imagine that you actually don't expect something to be null, and you catch (and log) a NullPointerException. This will not be very useful information, since many things can be null at that point.

Carl Manaster
  • 39,054
  • 15
  • 99
  • 149
Bozho
  • 572,413
  • 138
  • 1,043
  • 1,132
2

Better solution for me is to use java.util.Optional.map(..) to chain these checks : https://stackoverflow.com/a/67216752/1796826

Laloi
  • 429
  • 4
  • 11
1

There is no checkForNull method that you can write that will facilitate this (that's simply not how method invokation and argument evaluation works in Java).

You can break down the chained statements into multiple statements, checking at every step. However, perhaps a better solution is to not have these methods return null in the first place. There is something called the Null Object Pattern that you may want to use instead.

Related questions

Community
  • 1
  • 1
polygenelubricants
  • 364,035
  • 124
  • 554
  • 617
-1

Very old question, but still adding my suggestion:

I would suggest instead of getting the DoorKnob from the House you should try to let the DoorKnob be provided to this class from the calling code, or by creating a central lookup facility specifically for this purpose (e.g. a DoorKnob service)

Adriaan Koster
  • 15,325
  • 4
  • 44
  • 60
-1

implementing nullPointer try/catch with a Supplier you can send it all chain of get

public static <T> T getValue(Supplier<T> getFunction, T defaultValue) {
    try {
        return getFunction.get();
    } catch (NullPointerException ex) {
        return defaultValue;
    }
}

and then call it in this way.

ObjectHelper.getValue(() -> object1.getObject2().getObject3().getObject4()));
Omar Ruiz
  • 23
  • 1
-3
// Example
LazyObject.from(curr).apply(A.class, A::getB).apply(B.class, B::getC).apply(C.class, C::getD).to(String.class);

// LazyObject.java
public class LazyObject {

private Object value;

private LazyObject(Object object) {
    this.value = object;
}

public <F, T> LazyObject apply(Class<F> type, Function<F, T> func) {
    Object v = value;
    if (type.isInstance(v)) {
        value = func.apply(type.cast(v));
    } else {
        value = null; // dead here
    }
    return this;
}

public <T> void accept(Class<T> type, Consumer<T> consumer) {
    Object v = value;
    if (type.isInstance(v)) {
        consumer.accept(type.cast(v));
    }
}

public <T> T to(Class<T> type) {
    Object v = value;
    if (type.isInstance(v)) {
        return type.cast(v);
    }
    return null;
}

public static LazyObject from(Object object) {
    return new LazyObject(object);
}

}
Yuebing Cao
  • 143
  • 1
  • 4