91

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
  • 21,314
  • 3
  • 24
  • 44
  • 6
    If only you followed the law of Demeter. – Oded Aug 11 '10 at 12:59
  • 34
    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
  • 1
    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
  • 2
    The Law of Demeter is a terrible idea (which is why, thankfully, nobody uses it!). Leads to error-prone gobbledegook code. It's also anti-intuitive: if every instinct is telling you not to write classes like that, you probably shouldn't. But I really don't see how it would help null safe chaining anyway. It just pushes the multiple null checks into the method House::getDoorknob(Floor floor, CompassPoint wall, DoorType doorType). And the un-useability of that monstrous method says it all. – barneypitt Jul 27 '22 at 11:48
  • @barneypitt I think the LoD is not so much about pushing chained method calls into convenience methods, but about designing your classes in a way that reduces the need for this type of remote data grabbing. – Adriaan Koster Nov 10 '22 at 12:46
  • My point being, it's a _bad_ way to design classes. The intuitive design is pretty much always the best design. The design which presents the data to you in the way you expect data to be presented to you is the best design. The design which matches the way humans think about entities and their properties (that they are chained) is the best design. The LoD is the opposite of good design. – barneypitt Nov 11 '22 at 13:12
  • @barneypitt What you say implies that trying to achieve loose coupling is bad design. That is simply not true, as evidenced by the many legacy systems that have become so tightly coupled that nobody dares to change anything in fear of breaking something elsewhere unexpectedly. – Adriaan Koster Nov 18 '22 at 09:16
  • Of course I'm not saying loose coupling is a bad idea! I said the LoD is a bad idea. The LoD is one (anti-)pattern which promotes loose coupling, amongst dozens (maybe hundreds) of patterns which promote loose coupling. I assert that this *particular* (anti-)pattern's disadvantages (generating unintuitive spaghetti code) massively outweigh its advantages, which I explained in detail. – barneypitt Feb 21 '23 at 20:59
  • Use Optional - see answer https://stackoverflow.com/a/41145698/211614 – Michael Peterson Apr 03 '23 at 01:33

11 Answers11

140

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,658
  • 9
  • 54
  • 82
Johnny
  • 14,397
  • 15
  • 77
  • 118
  • 3
    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
  • 5
    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
  • This is the correct way to do it if you have Java 8+ available. At the end of the Optional chain you can still do .orElse(null); if you need to pass or otherwise handle a non-Optional type. There is really no downside to chaining ofNullable/map and this should be the accepted answer. – Michael Peterson Apr 03 '23 at 01:28
  • Alien magic !!! – Peter S. May 19 '23 at 09:32
23

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,658
  • 9
  • 54
  • 82
Roman Seleznov
  • 430
  • 4
  • 6
  • 6
    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
  • 7
    Isn't NullpointerException expensive than a null check ? – user3044440 Dec 11 '20 at 21:21
  • Don't resort to this unless you are using a really old version of Java (<8 where Optional isn't available). See the answer involving Optional.ofNullable()/.map() – Michael Peterson Apr 03 '23 at 01:31
  • This approach is effective when you have a complex data structure, where the majority of the data is optional. Like accessing nested JAXB xml elements. 2 things though: Using `.getFloor(0)`, I'd also handle `IndexOutOfBoundsException` the same way. Returning with an `Optional` would be more elegant. – sanya Aug 25 '23 at 12:38
14

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,783
  • 1
  • 22
  • 30
  • 52
    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
9

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,989
  • 7
  • 48
  • 78
Carl Manaster
  • 39,912
  • 17
  • 102
  • 155
  • 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
  • 6
    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,912
  • 17
  • 102
  • 155
Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
4

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

Laloi
  • 506
  • 1
  • 4
  • 12
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
  • 376,812
  • 128
  • 561
  • 623
0

You could potentially have a generic method like below:

public static <T> void ifPresentThen(final Supplier<T> supplier, final Consumer<T> consumer) {
    T value;
    try {
        value = supplier.get();
    } catch (NullPointerException e) {
        // Don't consume "then"
        return;
    }
    consumer.accept(value);
}

So now you would be able to do

ifPresentThen(
    () -> house.getFloor(0).getWall(WEST).getDoor().getDoorknob(),
    doorKnob -> doSomething());
-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

Very old question, but still adding my suggestion:

I would suggest instead of getting the DoorKnob from deep within the House in one method call chain, 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)

Simplified example of design with loose coupling:

class Architect {

    FloorContractor floorContractor;

    void build(House house) {
        for(Floor floor: house.getFloors()) {
            floorContractor.build(floor);
        }
    }    
}

class FloorContractor {

    DoorMaker doorMaker;

    void build(Floor floor) {
        for(Wall wall: floor.getWalls()) {
            if (wall.hasDoor()) {
                doorMaker.build(wall.getDoor());
            }
        }
    } 
}

class DoorMaker {

    Tool tool;

    void build(Door door) {
        tool.build(door.getFrame());
        tool.build(door.getHinges());
        tool.build(door.getDoorKnob());
    }        
}
Adriaan Koster
  • 15,870
  • 5
  • 45
  • 60
-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
  • 173
  • 1
  • 4