Java的:避免嵌套类检查空(深空检查)(Java: avoid checking for null

2019-06-24 01:50发布

想象一下,我有一个类家族。 它包含人的名单。 每一个(类)人包含一个(类)地址。 每一个(类)地址包含一个(类)邮编。 任何“中间”级可以为空。

那么,有没有一种简单的方法去POSTALCODE,而无需在每一步检查null? 也就是说,有没有办法避免以下菊花链代码? 我知道有没有“本地”的Java解决方案,但希望如果有人库之类的东西都知道。 (选中共享和番石榴,并没有看到任何东西)

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!!!
                }
            }
        }
    }
}

不,不能改变结构。 这是从一个服务,我没有控制权。

不,我不能使用Groovy和它的方便“猫王”操作符。

不,我不希望等待的Java 8:d

我不能相信我是第一个开发过生病“N累了写这样的代码,但我一直没能找到解决的办法。

想法?

谢谢

-
llappall

Answer 1:

您的代码的行为一样

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
}

由于短路的评价 ,这也是安全的,因为第二个条件将不被评估,如果第一个是假的,第三个将不如果第二是假的评估,...。并且因为如果你不会得到NPE它。



Answer 2:

你可以得到的最接近的是采取的条件语句中的短切规则的优势:

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!!!

}

顺便说一句,捕获异常,而不是预先测试条件是一个可怕的想法。



Answer 3:

如果它是罕见的,你可以忽略null的检查和依靠NullPointerException 。 “稀有金属”由于可能的性能问题(取决于,通常会在堆栈跟踪其可以是昂贵的填)。

除此之外,1)特定的帮手检查空清理代码或2)使用反射和串像做通用的方法方法:

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

实现留作练习。



Answer 4:

没有这样一个很酷的想法,但对捕捉异常如何:

    try 
    {
        PostalCode pc = people.get(0).getAddress().getPostalCode();
    }
    catch(NullPointerException ex)
    {
        System.out.println("Gotcha");
    }


Answer 5:

您可以使用:

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

可选相当于:

Optional.ofNullable(product).map(
            Product::getLatestVersion
        ).map(
            ProductVersion::getProductData
        ).map(
            ProductData::getTradeItem
        ).map(
            TradeItemType::getInformationProviderOfTradeItem
        ).map(
            PartyInRoleType::getGln
        ).orElse(null);


Answer 6:

相反,使用空的,你可以使用“空对象”设计模式的一些版本。 例如:

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

那么你的if语句可以成为:

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

这是不理想的,因为:

  • 你坚持做NULL对象为每个类,
  • 很难使这些对象通用的,所以你坚持做,你要使用的每个列表,地图等的空对象版本,
  • 有可能与子类一些有趣的问题和NULL使用。

但如果你真的恨你== null S,这是一个出路。



Answer 7:

我只是在寻找同样的事情(我的背景:一束自动创建JAXB类的,并不知我有这些长菊花链.getFoo().getBar()...不变的是,曾经在的,而一个。在中间返回NULL的调用,从而导致NPE。

这是我开始了一段时间回来是基于反射摆弄。 我相信我们可以让这个更漂亮,更高效(高速缓存反映,一两件事,也界定“神奇”的方法,如._all对集合中的所有元素自动循环,如果在中途返回一些方法集合)。 不漂亮,但也许有人能告诉我们,如果已经有更好的东西在那里:

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


Answer 8:

如果,万一,你正在使用java8那么你可以使用;

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: 避免无效检查



Answer 9:

虽然这个职位是近五年来的时候,我可能有另一种解决方案是如何处理老问题NullPointerException秒。

简而言之:

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

由于有大量的遗留代码仍然在使用,使用Java 8和Optional并不总是一个选项。

每当有涉及深嵌套类(JAXB,SOAP,JSON,你的名字......)和得墨忒耳定律不适用,你基本上要检查一切,看看是否有可能NPE的虎视眈眈。

我提出的解决方案力求可读性和如果没有至少3个以上的嵌套类涉及(当我说嵌套的,我的意思不是不应该使用嵌套类的形式背景)。 由于代码读取超过它写的是一个快速浏览的代码的左侧部分会使它的意义比使用深度嵌套的if-else语句更清晰。

如果您需要else部分,您可以使用此模式:

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

某些集成开发环境将打破这种格式,除非你指示他们不要(见这个问题 )。

你的条件语句必须是反向的 - 你告诉代码时,它应该打破,而不是当它应该继续。

还有一两件事 - 你的代码仍然容易断裂。 您必须使用if(family.getPeople() != null && !family.getPeople().isEmpty())作为你的第一行代码,否则空单将抛出NPE。



Answer 10:

我最喜欢的,简单的try / catch,以避免嵌套null检查...

try {
    if(order.getFulfillmentGroups().get(0).getAddress().getPostalCode() != null) {
        // your code
    } 
} catch(NullPointerException|IndexOutOfBoundsException e) {}


文章来源: Java: avoid checking for null in nested classes (Deep Null checking)