Java 如何避免在方法链接中检查空值?

Java 如何避免在方法链接中检查空值?,java,exception-handling,nullpointerexception,Java,Exception Handling,Nullpointerexception,我需要检查某个值是否为null。如果不是null,那么只需将某个变量设置为true。这里没有其他声明。我有太多这样的状况检查 有没有办法在不检查所有方法返回值的情况下处理此空检查 if(country != null && country.getCity() != null && country.getCity().getSchool() != null && country.getCity().getSchool().getStudent() !

我需要检查某个值是否为null。如果不是null,那么只需将某个变量设置为true。这里没有其他声明。我有太多这样的状况检查

有没有办法在不检查所有方法返回值的情况下处理此空检查

if(country != null && country.getCity() != null && country.getCity().getSchool() != null && country.getCity().getSchool().getStudent() != null .....) {
    isValid = true;
}
我认为直接检查变量并忽略NullpointerException。这是一种好的做法吗

try{
    if(country.getCity().getSchool().getStudent().getInfo().... != null)
} catch(NullPointerException ex){
    //dont do anything.
}

不,在Java中,捕获NPE而不是空检查引用通常不是好的做法

如果您愿意,您可以使用:

if (Optional.ofNullable(country)
            .map(Country::getCity)
            .map(City::getSchool)
            .map(School::getStudent)
            .isPresent()) {
    isValid = true;
}
或者干脆

boolean isValid = Optional.ofNullable(country)
                          .map(Country::getCity)
                          .map(City::getSchool)
                          .map(School::getStudent)
                          .isPresent();

如果这就是
isValid
应该检查的所有内容。

您可以在这里使用
Optional
,但它会在每个步骤创建一个可选对象

boolean isValid = Optional.ofNullable(country)
    .map(country -> country.getCity()) //Or use method reference Country::getCity
    .map(city -> city.getSchool())
    .map(school -> school.getStudent())
    .map(student -> true)
    .orElse(false);

//OR
boolean isValid = Optional.ofNullable(country)
                      .map(..)
                      ....
                      .isPresent();

作为
可选
的其他精细用法的替代方法,我们还可以使用带有
供应商
变量args作为参数的实用方法。
这是有道理的,因为我们在对象中没有很多嵌套级别要检查,但有很多字段要检查。
此外,当检测到
null
时,可以很容易地修改它以记录/处理某些内容

    boolean isValid = isValid(() -> address, // first level
                              () -> address.getCity(),   // second level
                              () -> address.getCountry(),// second level
                              () -> address.getStreet(), // second level
                              () -> address.getZip(),    // second level
                              () -> address.getCountry() // third level
                                           .getISO()


@SafeVarargs
public static boolean isValid(Supplier<Object>... suppliers) {
    for (Supplier<Object> supplier : suppliers) {
        if (Objects.isNull(supplier.get())) {
            // log, handle specific thing if required
            return false;
        }
    }
    return true;
}
boolean-isValid=isValid(()->地址,//第一级
()->address.getCity(),//第二级
()->address.getCountry(),//第二级
()->address.getStreet(),//第二级
()->address.getZip(),//第二级
()->address.getCountry()//第三级
.getISO()
@安全变量
公共静态布尔值有效(供应商…供应商){
对于(供应商:供应商){
if(Objects.isNull(supplier.get())){
//记录,如果需要,处理特定的事情
返回false;
}
}
返回true;
}

假设您想添加一些跟踪,您可以这样写:

boolean isValid = isValid(  Arrays.asList("address", "city", "country",
                                          "street", "zip", "Country ISO"),
                            () -> address, // first level
                            () -> address.getCity(),   // second level
                            () -> address.getCountry(),// second level
                            () -> address.getStreet(), // second level
                            () -> address.getZip(),    // second level
                            () -> address.getCountry() // third level
                                         .getISO()
                         );


@SafeVarargs
public static boolean isValid(List<String> fieldNames, Supplier<Object>... suppliers) {
    if (fieldNames.size() != suppliers.length){
         throw new IllegalArgumentException("...");
    }
    for (int i = 0; i < suppliers.length; i++) {
        if (Objects.isNull(suppliers.get(i).get())) {
            LOGGER.info( fieldNames.get(i) + " is null");
            return false;
        }
    }
    return true;
}
boolean isValid=isValid(Arrays.asList(“地址”、“城市”、“国家”),
“街道”、“邮编”、“国家ISO”),
()->地址,//第一级
()->address.getCity(),//第二级
()->address.getCountry(),//第二级
()->address.getStreet(),//第二级
()->address.getZip(),//第二级
()->address.getCountry()//第三级
.getISO()
);
@安全变量
公共静态布尔值有效(列出字段名、供应商…供应商){
if(fieldNames.size()!=suppliers.length){
抛出新的IllegalArgumentException(“…”);
}
对于(int i=0;iJava没有“空安全”操作,例如

您可以:

  • 抓住NPE并忽略它
  • 手动检查所有引用
  • 根据其他答案使用可选选项
  • 使用诸如XLST之类的工具

否则,如果您可以控制域对象,则可以重新设计类,以便从顶级对象中获得所需的信息(使
Country
class执行所有空检查…)面向对象的方法是将isValid方法放在Country和其他类中。它不会减少空检查的数量,但每个方法只有一个,您不会重复它们

public boolean isValid() {
  return city != null && city.isValid();
}
这假设验证在使用您所在国家/地区的任何地方都是相同的,但通常情况下都是如此。如果不是,则该方法应命名为hasStudent(),但这不太通用,并且您可能会在该国家/地区复制整个学校界面。例如,在另一个地方,您可能需要hasTeacher()或hasCourse()

另一种方法是使用空对象:

public class Country {
  public static final Country NO_COUNTRY = new Country();

  private City city = City.NO_CITY;

  // etc.
}
我不确定在这种情况下是否更可取(严格来说,您需要一个子类来覆盖所有修改方法),Java 8的方法是在其他答案中使用Optional as方法,但我建议更全面地接受它:

private Optional<City> city = Optional.ofNullable(city);

public Optional<City> getCity() {
   return city;
}
private Optional city=Optional.ofNullable(城市);
公共可选getCity(){
回归城市;
}
如果总是使用空对象而不是空对象(注意字段初始化),则只能使用空对象和可空对象。否则,您仍然需要空检查。因此,此选项可以避免空检查,但在其他位置,由于减少了空检查,您的代码会变得更加冗长


当然,正确的设计可能是在可能的情况下使用集合(而不是可选的)。一个国家有一套城市,一套城市有一套学校,有一套学生等等。

你也可以看看vavr的选项,正如下面的文章所描述的,它比Java的可选选项更好,并且有更丰富的API


由于Java8中有optionalwell,因此在
getCity()上可以得到
null
值。这将导致NPE,你不知道什么是“代码> null <代码>,只是作为一个信息点:在创建这些类型的链之前你应该考虑。这个代码是一个巨大的红旗:如果你发现你自己写这么深的嵌套链,停止,重新考虑,并重新编写你的代码。最好的解决办法是消除这个问题。m:实现这些方法,使它们永远不会返回null。如果它们永远不会返回null,那么您就不必检查它。为什么null首先是一个合法的返回值?请注意,只有在调用代码之前,如果
isValid
is
false
,您的两个示例才是等效的!“不,在Java中允许NPE发生不是一个好的做法。”我不同意。如果传入的数据无效,应该抛出异常。A