llappall
llappall

Reputation: 2952

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

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.

Upvotes: 76

Views: 52780

Answers (14)

awgtek
awgtek

Reputation: 1829

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

Upvotes: 1

Oskarro
Oskarro

Reputation: 403

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);

Upvotes: 7

Brian
Brian

Reputation: 616

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 :)

Upvotes: 6

nitesh c
nitesh c

Reputation: 79

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

Upvotes: 0

Keith Klingeman
Keith Klingeman

Reputation: 21

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) {}

Upvotes: -3

Robert
Robert

Reputation: 539

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);

Upvotes: 53

darioo
darioo

Reputation: 47183

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.

Upvotes: 2

Amit Kumar Gupta
Amit Kumar Gupta

Reputation: 7413

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

Upvotes: 11

Pierre D
Pierre D

Reputation: 26211

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);
    }
}

Upvotes: 0

charleyc
charleyc

Reputation: 1709

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.

Upvotes: 1

Paul Tomblin
Paul Tomblin

Reputation: 182792

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.

Upvotes: 6

Mattias Isegran Bergander
Mattias Isegran Bergander

Reputation: 11909

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.

Upvotes: 0

amit
amit

Reputation: 178431

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.

Upvotes: 23

MByD
MByD

Reputation: 137312

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");
    }

Upvotes: 0

Related Questions