Java: avoid checking for null in nested classes (Deep Null checking)
Asked Answered
U

14

73

Imagine I have a class Family. It contains a List of Person. Each (class) Person contains a (class) Address. Each (class) Address contains a (class) PostalCode. Any "intermediate" class can be null.

So, is there a simple way to get to PostalCode without having to check for null in every step? i.e., is there a way to avoid the following daisy chaining code? I know there's not "native" Java solution, but was hoping if anyone knows of a library or something. (checked Commons & Guava and didn't see anything)

if(family != null) {
    if(family.getPeople() != null) {
        if(family.people.get(0) != null) {
            if(people.get(0).getAddress() != null) {
                if(people.get(0).getAddress().getPostalCode() != null) {
                    //FINALLY MADE IT TO DO SOMETHING!!!
                }
            }
        }
    }
}

No, can't change the structure. It's from a service I don't have control over.

No, I can't use Groovy and it's handy "Elvis" operator.

No, I'd prefer not to wait for Java 8 :D

I can't believe I'm the first dev ever to get sick 'n tired of writing code like this, but I haven't been able to find a solution.

Ure answered 30/4, 2012 at 22:29 Comment(7)
Sorry, you're stuck. Some people use the trinary conditional operator to make it a little less dense, but it's still the same bytecode, just harder to read.Cupel
"I can't believe I'm the first dev ever to get sick 'n tired of writing code like this" Well, you're not.Parasitize
Sure. But I don't believe you can more beaty the code! Sorry!Sites
Despite all of the answers telling you to ignore null checks and just try to catch a NullPointerException, don't do it! Your code may be an eyesore, but throwing an exception is an expensive operation that you always want to avoid if you can.Osteen
Also, consider all the good things you can do in "else" clauses if you put them in place - error messaging, alternative code paths, etc. Given all that, it won't look as bad.Columbus
If only you could port Brototype for Java... github.com/letsgetrandy/brototypeIdioblast
Not to offend anyone but this is why I love TypeScript!Equalizer
P
53

You can use for:

product.getLatestVersion().getProductData().getTradeItem().getInformationProviderOfTradeItem().getGln();

optional equivalent:

Optional.ofNullable(product).map(
            Product::getLatestVersion
        ).map(
            ProductVersion::getProductData
        ).map(
            ProductData::getTradeItem
        ).map(
            TradeItemType::getInformationProviderOfTradeItem
        ).map(
            PartyInRoleType::getGln
        ).orElse(null);
Pufahl answered 22/1, 2019 at 5:55 Comment(6)
It requires API 24 and above :(Ursulaursulette
If you see .map implementation, it uses Objects.requireNonNull(mapper); and this method throws nullpointerexception if the mapper goes null.Phagy
@Phagy the mapper, in this case, is the method reference and is never null. The value returned by the mapper can be null, and in that case the map returns Optional.empty()Ephebe
This should be the accepted answer..Buttonball
This arguably doesn't add any benefit. Whats wrong with the traditional || and if-elseSlam
This should be definitely accepted answer. Optional is class defined exactly for dealing with nulls.Hugmetight
F
22

Your code behaves the same as

if(family != null &&
  family.getPeople() != null &&
  family.people.get(0) != null && 
  family.people.get(0).getAddress() != null &&
  family.people.get(0).getAddress().getPostalCode() != null) { 
       //My Code
}

Thanks to short circuiting evaluation, this is also safe, since the second condition will not be evaluated if the first is false, the 3rd won't be evaluated if the 2nd is false,.... and you will not get NPE because if it.

Formaldehyde answered 30/4, 2012 at 22:35 Comment(0)
R
10

If, in case, you are using java8 then you may use;

resolve(() -> people.get(0).getAddress().getPostalCode());
    .ifPresent(System.out::println);

:
public static <T> Optional<T> resolve(Supplier<T> resolver) {
    try {
        T result = resolver.get();
        return Optional.ofNullable(result);
    }
    catch (NullPointerException e) {
        return Optional.empty();
    }
}

REF: avoid null checks

Retrorse answered 12/10, 2016 at 4:19 Comment(4)
This solution relies on catching the NPE which will perform very poorly.Bristol
I am in favour of this. Can someone explain why it's poor? Is it because the getters might internally through an NPE due to a different bug, and it will get masked?Hal
I guess what @Bristol is saying is NPEs are runtime exceptions and hence not supposed to be caught. But in this case I can probably live with this approach as it is saving a lot of code. Although I think this approach will mask the real point where null occurred, so in that way it is not so good. But if the caller of the method only cares about whether the final value (postalCode) can be retrieved or not, then it is fine.Conjugal
@Hal catching an exception can be orders of magnitude more expensive than an "if" statement, so using a catch-block as a programming solution is not recommended. Exceptions are designed for "exceptional" cases, not the normal program flow. Having said that, however, there are cases where it might make sense as long as you understand the performance disadvantages and can live with it. Here's a good explanation of the tradeoffs: #18266440Bristol
T
7

You can get rid of all those null checks by utilizing the Java 8 Optional type.

The stream method - map() accepts a lambda expression of type Function and automatically wraps each function result into an Optional. That enables us to pipe multiple map operations in a row. Null checks are automatically handled under the neath.

Optional.of(new Outer())
  .map(Outer::getNested)
  .map(Nested::getInner)
  .map(Inner::getFoo)
  .ifPresent(System.out::println);

We also have another option to achieve the same behavior is by utilizing a supplier function to resolve the nested path:

public static <T> Optional<T> resolve(Supplier<T> resolver) {
  try {
      T result = resolver.get();
      return Optional.ofNullable(result);
  }
  catch (NullPointerException e) {
      return Optional.empty();
  }
}

How to invoke new method? Look below:

Outer obj = new Outer();
obj.setNested(new Nested());
obj.getNested().setInner(new Inner());

resolve(() -> obj.getNested().getInner().getFoo())
    .ifPresent(System.out::println);
Trachytic answered 12/10, 2022 at 19:59 Comment(0)
C
6

The closest you can get is to take advantage of the short-cut rules in conditionals:

if(family != null && family.getPeople() != null && family.people.get(0) != null  && family.people.get(0).getAddress() != null && family.people.get(0).getAddress().getPostalCode() != null) {
                    //FINALLY MADE IT TO DO SOMETHING!!!

}

By the way, catching an exception instead of testing the condition in advance is a horrible idea.

Cupel answered 30/4, 2012 at 22:34 Comment(1)
you've got some accessive } there (forgot to remove them when refactored)Formaldehyde
B
4

I personally prefer something similar to:

nullSafeLogic(() -> family.people.get(0).getAddress().getPostalCode(), x -> doSomethingWithX(x))

public static <T, U> void nullSafeLogic(Supplier<T> supplier, Function<T,U> function) {
    try {
        function.apply(supplier.get());
    } catch (NullPointerException n) {
        return null;
    }
}

or something like

nullSafeGetter(() -> family.people.get(0).getAddress().getPostalCode())

public static <T> T nullSafeGetter(Supplier<T> supplier) {
    try {
        return supplier.get();
    } catch (NullPointerException n) {
        return null;
    }
}

Best part is the static methods are reusable with any function :)

Boisvert answered 10/2, 2022 at 2:32 Comment(0)
R
2

Although this post is almost five years old, I might have another solution to the age old question of how to handle NullPointerExceptions.

In a nutshell:

end: {
   List<People> people = family.getPeople();            if(people == null || people.isEmpty()) break end;
   People person = people.get(0);                       if(person == null) break end;
   Address address = person.getAddress();               if(address == null) break end;
   PostalCode postalCode = address.getPostalCode();     if(postalCode == null) break end;

   System.out.println("Do stuff");
}

Since there is a lot of legacy code still in use, using Java 8 and Optional isn't always an option.

Whenever there are deeply nested classes involved (JAXB, SOAP, JSON, you name it...) and Law of Demeter isn't applied, you basically have to check everything and see if there are possible NPEs lurking around.

My proposed solution strives for readibility and shouldn't be used if there aren't at least 3 or more nested classes involved (when I say nested, I don't mean Nested classes in the formal context). Since code is read more than it is written, a quick glance to the left part of the code will make its meaning more clear than using deeply nested if-else statements.

If you need the else part, you can use this pattern:

boolean prematureEnd = true;

end: {
   List<People> people = family.getPeople();            if(people == null || people.isEmpty()) break end;
   People person = people.get(0);                       if(person == null) break end;
   Address address = person.getAddress();               if(address == null) break end;
   PostalCode postalCode = address.getPostalCode();     if(postalCode == null) break end;

   System.out.println("Do stuff");
   prematureEnd = false;
}

if(prematureEnd) {
    System.out.println("The else part");
}

Certain IDEs will break this formatting, unless you instruct them not to (see this question).

Your conditionals must be inverted - you tell the code when it should break, not when it should continue.

One more thing - your code is still prone to breakage. You must use if(family.getPeople() != null && !family.getPeople().isEmpty()) as the first line in your code, otherwise an empty list will throw a NPE.

Rennin answered 30/1, 2017 at 7:7 Comment(0)
S
1

Instead of using null, you could use some version of the "null object" design pattern. For example:

public class Family {
    private final PersonList people;
    public Family(PersonList people) {
        this.people = people;
    }

    public PersonList getPeople() {
        if (people == null) {
            return PersonList.NULL;
        }
        return people;
    }

    public boolean isNull() {
        return false;
    }

    public static Family NULL = new Family(PersonList.NULL) {
        @Override
        public boolean isNull() {
            return true;
        }
    };
}


import java.util.ArrayList;

public class PersonList extends ArrayList<Person> {
    @Override
    public Person get(int index) {
        Person person = null;
        try {
            person = super.get(index);
        } catch (ArrayIndexOutOfBoundsException e) {
            return Person.NULL;
        }
        if (person == null) {
            return Person.NULL;
        } else {
            return person;
        }
    }
    //... more List methods go here ...

    public boolean isNull() {
        return false;
    }

    public static PersonList NULL = new PersonList() {
        @Override
        public boolean isNull() {
            return true;
        }
    };
}

public class Person {
    private Address address;

    public Person(Address address) {
        this.address = address;
    }

    public Address getAddress() {
        if (address == null) {
            return Address.NULL;
        }
        return address;
    }
    public boolean isNull() {
        return false;
    }

    public static Person NULL = new Person(Address.NULL) {
        @Override
        public boolean isNull() {
            return true;
        }
    };
}

etc etc etc

Then your if statement can become:

if (!family.getPeople().get(0).getAddress().getPostalCode.isNull()) {...}

It's suboptimal since:

  • You're stuck making NULL objects for every class,
  • It's hard to make these objects generic, so you're stuck making a null-object version of each List, Map, etc that you want to use, and
  • There are potentially some funny issues with subclassing and which NULL to use.

But if you really hate your == nulls, this is a way out.

Sot answered 1/5, 2012 at 1:38 Comment(0)
M
0

Not such a cool idea, but how about catching the exception:

    try 
    {
        PostalCode pc = people.get(0).getAddress().getPostalCode();
    }
    catch(NullPointerException ex)
    {
        System.out.println("Gotcha");
    }
Mccann answered 30/4, 2012 at 22:33 Comment(1)
I can't think of a situation where it is ok to catch a NullPointerException (maybe there are a few exceptions), it is a bad practice because generally a NPE is a programming error, a correct program should not generate one, when the programmer just catches the NPE it feels like he is trying to cover it up.Perspective
K
0

If it is rare you could ignore the null checks and rely on NullPointerException. "Rare" due to possible performance problem (depends, usually will fill in stack trace which can be expensive).

Other than that 1) a specific helper method that checks for null to clean up that code or 2) Make generic approach using reflection and a string like:

checkNonNull(family, "people[0].address.postalcode")

Implementation left as an exercise.

Kosey answered 30/4, 2012 at 22:37 Comment(2)
Reflection isn't exactly cheap either.Cupel
Yes indeed it can be slow as well @paul . Especially method lookup etc. Actual method invocation though can be quite fast, but again it depends (other optimizations might be harder for the VM). So cacheing the method/field lookups is usually important from my experience if it is needed. Most of all it of course depends on how often the code is used at all.Kosey
Z
0

I was just looking for the same thing (my context: a bunch of automatically created JAXB classes, and somehow I have these long daisy-chains of .getFoo().getBar().... Invariably, once in a while one of the calls in the middle return null, causing NPE.

Something I started fiddling with a while back is based on reflection. I'm sure we can make this prettier and more efficient (caching the reflection, for one thing, and also defining "magic" methods such as ._all to automatically iterate on all the elements of a collection, if some method in the middle returns a collection). Not pretty, but perhaps somebody could tell us if there is already something better out there:

/**
 * Using {@link java.lang.reflect.Method}, apply the given methods (in daisy-chain fashion)
 * to the array of Objects x.
 * 
 * <p>For example, imagine that you'd like to express:
 * 
 * <pre><code>
 * Fubar[] out = new Fubar[x.length];
 * for (int i=0; {@code i<x.length}; i++) {
 *   out[i] = x[i].getFoo().getBar().getFubar();
 * }
 * </code></pre>
 * 
 * Unfortunately, the correct code that checks for nulls at every level of the
 * daisy-chain becomes a bit convoluted.
 * 
 * <p>So instead, this method does it all (checks included) in one call:
 * <pre><code>
 * Fubar[] out = apply(new Fubar[0], x, "getFoo", "getBar", "getFubar");
 * </code></pre>
 * 
 * <p>The cost, of course, is that it uses Reflection, which is slower than
 * direct calls to the methods.
 * @param type the type of the expected result
 * @param x the array of Objects
 * @param methods the methods to apply
 * @return
 */
@SuppressWarnings("unchecked")
public static <T> T[] apply(T[] type, Object[] x, String...methods) {
    int n = x.length;
    try {
        for (String methodName : methods) {
            Object[] out = new Object[n];
            for (int i=0; i<n; i++) {
                Object o = x[i];
                if (o != null) {
                    Method method = o.getClass().getMethod(methodName);
                    Object sub = method.invoke(o);
                    out[i] = sub;
                }
            }
            x = out;
        }
    T[] result = (T[])Array.newInstance(type.getClass().getComponentType(), n);
    for (int i=0; i<n; i++) {
            result[i] = (T)x[i];
    }
            return result;
    } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
            throw new RuntimeException(e);
    }
}
Zoophobia answered 26/4, 2013 at 22:14 Comment(0)
M
0

If you can use groovy for mapping it will clean up the syntax and codes looks cleaner. As Groovy co-exist with java you can leverage groovy for doing the mapping.

if(family != null) {
    if(family.getPeople() != null) {
        if(family.people.get(0) != null) {
            if(people.get(0).getAddress() != null) {
                if(people.get(0).getAddress().getPostalCode() != null) {
                    //FINALLY MADE IT TO DO SOMETHING!!!
                }
            }
        }
    }
}

instead you can do this

if(family?.people?[0]?.address?.postalCode) {
   //do something
}

or if you need to map it to other object

somobject.zip = family?.people?[0]?.address?.postalCode
Merridie answered 17/8, 2020 at 15:15 Comment(0)
D
0

Following uses BeanUtils (reflection) to solve this particular use case, i.e. containing nested lists. Note: must be able to use a string to define the fully qualified field.

package com.example.demo.util;

import lombok.Data;
import org.apache.commons.beanutils.PropertyUtils;

import java.util.ArrayList;
import java.util.List;

public class NestedNullSafeGetter {

    public static void main(String[] s) {
        Family family = new Family();
        family.people = new ArrayList<>();
        family.people.add(new Person());
        family.people.get(0).address = new Address();
        family.people.get(0).address.postalCode = "55555";

        String postalCodeField = "people.address.postalCode";

        String postalCodeValue = (String) findNestedValue(family, postalCodeField);
        System.out.println("postalCodeValue = " + postalCodeValue);
        family.people.get(0).address.postalCode = null;
        postalCodeValue = (String) findNestedValue(family, postalCodeField);
        System.out.println("postalCodeValue = " + postalCodeValue);
        family.people.get(0).address = null;
        postalCodeValue = (String) findNestedValue(family, postalCodeField);
        System.out.println("postalCodeValue = " + postalCodeValue);
        family.people.set(0, null);
        postalCodeValue = (String) findNestedValue(family, postalCodeField);
        System.out.println("postalCodeValue = " + postalCodeValue);
        family.people.clear();
        postalCodeValue = (String) findNestedValue(family, postalCodeField);
        System.out.println("postalCodeValue = " + postalCodeValue);
        family.people = null;
        postalCodeValue = (String) findNestedValue(family, postalCodeField);
        System.out.println("postalCodeValue = " + postalCodeValue);
        family = null;
        postalCodeValue = (String) findNestedValue(family, postalCodeField);
        System.out.println("postalCodeValue = " + postalCodeValue);
    }

    public static Object findNestedValue(Object object, String fieldStr) {
        if (object == null) {
            System.out.println("root was null");
            return null;
        }
        if (fieldStr.contains(".")) {
            String key = fieldStr.substring(0, fieldStr.indexOf('.'));
            Object subObject = safeGetProperty(object, key);
            if (subObject == null) {
                System.out.println(key + " was null");
                return null;
            } else {
                if (subObject instanceof List) {
                    List list = (List) subObject;
                    if (list.isEmpty()) {
                        System.out.println(key + " was empty");
                        return null;
                    } else {
                        Object firstListItem = list.get(0);
                        if (firstListItem == null) {
                            System.out.println(key + " first item was null");
                            return null;
                        } else {
                            return findNestedValue(firstListItem, fieldStr.substring(fieldStr.indexOf('.') + 1));
                        }
                    }
                } else {
                    return findNestedValue(subObject, fieldStr.substring(fieldStr.indexOf('.') + 1));
                }
            }
        } else {
            return safeGetProperty(object, fieldStr);
        }
    }

    private static Object safeGetProperty(Object object, String fieldStr) {
        try {
            return PropertyUtils.getProperty(object, fieldStr);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Data
    public static class Family {
        List<Person> people;
    }

    @Data
    public static class Person {
        Address address;
    }

    @Data
    public static class Address {
        String postalCode;
    }

}

Output:

postalCodeValue = 55555
postalCodeValue = null
address was null
postalCodeValue = null
people first item was null
postalCodeValue = null
people was empty
postalCodeValue = null
people was null
postalCodeValue = null
root was null
postalCodeValue = null
Dorinedorion answered 20/3 at 20:9 Comment(0)
C
-3

and my favorite, the simple try/catch, to avoid nested null checks...

try {
    if(order.getFulfillmentGroups().get(0).getAddress().getPostalCode() != null) {
        // your code
    } 
} catch(NullPointerException|IndexOutOfBoundsException e) {}
Competitor answered 7/3, 2019 at 22:14 Comment(1)
This will never be a suggested approach!!Phagy

© 2022 - 2024 — McMap. All rights reserved.