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

前端 未结 11 454
失恋的感觉
失恋的感觉 2020-12-05 09:12

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 "i

相关标签:
11条回答
  • 2020-12-05 10:04

    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);
        }
    }
    
    0 讨论(0)
  • 2020-12-05 10:04

    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) {}
    
    0 讨论(0)
  • 2020-12-05 10:10

    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");
        }
    
    0 讨论(0)
  • 2020-12-05 10:13

    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

    0 讨论(0)
  • 2020-12-05 10:14

    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
    
    0 讨论(0)
提交回复
热议问题