我使用x!=null以避免NullPointerException。还有其他选择吗?

if (x != null) {
    // ...
}

当前回答

哇,当我们有57种不同的方式来推荐NullObject模式时,我几乎不想再加一个答案,但我想,一些对这个问题感兴趣的人可能想知道,有人提议为Java7添加“空安全处理”,这是一种精简的语法,如果不等于空逻辑的话。

Alex Miller给出的示例如下:

public String getPostcode(Person person) {  
  return person?.getAddress()?.getPostcode();  
}  

这个表示仅在左标识符不为空时取消引用,否则将表达式的其余部分求值为空。一些人,如Java Pose成员迪克·沃尔(Dick Wall)和德文郡的选民,真的很喜欢这个提议,但也有人反对,理由是它实际上会鼓励更多地使用null作为前哨值。


更新:在Project Coin下提交了一份关于Java 7中空安全运算符的官方提案。语法与上面的示例稍有不同,但概念相同。


更新:空安全运营商提议未纳入Project Coin。因此,您不会在Java7中看到这种语法。

其他回答

如果不允许未定义的值:

您可以配置IDE以警告您可能的空取消引用。例如,在Eclipse中,请参阅首选项>Java>编译器>错误/警告/空分析。

如果允许未定义的值:

如果您想定义一个新的API,其中未定义的值是有意义的,请使用OptionPattern(可能在函数语言中很熟悉)。它具有以下优点:

API中明确说明是否存在输入或输出。编译器强制您处理“未定义”的情况。选项是monad,因此不需要进行冗长的空检查,只需使用map/foreach/getOrElse或类似的组合符即可安全地使用该值(示例)。

Java 8内置了可选类(推荐);对于早期版本,有一些库选项,例如Guava的Optional或FunctionalJava的Option。但是,像许多函数样式模式一样,在Java中使用Option(甚至是8)会产生一些样板,您可以使用不那么冗长的JVM语言(例如Scala或Xtend)来减少这些样板。

如果必须处理可能返回null的API,那么在Java中就做不了什么了。Xtend和Groovy有Elvis运算符?:和空安全解引用运算符?。,但请注意,如果引用为null,则返回null,因此它只是“延迟”了对null的正确处理。

哇,当我们有57种不同的方式来推荐NullObject模式时,我几乎不想再加一个答案,但我想,一些对这个问题感兴趣的人可能想知道,有人提议为Java7添加“空安全处理”,这是一种精简的语法,如果不等于空逻辑的话。

Alex Miller给出的示例如下:

public String getPostcode(Person person) {  
  return person?.getAddress()?.getPostcode();  
}  

这个表示仅在左标识符不为空时取消引用,否则将表达式的其余部分求值为空。一些人,如Java Pose成员迪克·沃尔(Dick Wall)和德文郡的选民,真的很喜欢这个提议,但也有人反对,理由是它实际上会鼓励更多地使用null作为前哨值。


更新:在Project Coin下提交了一份关于Java 7中空安全运算符的官方提案。语法与上面的示例稍有不同,但概念相同。


更新:空安全运营商提议未纳入Project Coin。因此,您不会在Java7中看到这种语法。

在Java8中,如果局部变量/字段/方法参数/方法返回类型从未赋值为null(并且不检查null),则可以使用类型T;如果可以为null,则可以键入Optional<T>。然后使用方法map处理T->,使用方法flatMap处理T->可选<R>:

class SomeService {
    @Inject
    private CompanyDao companyDao;

    // return Optional<String>
    public Optional<String> selectCeoCityByCompanyId0(int companyId) {
        return companyDao.selectById(companyId)
                .map(Company::getCeo)
                .flatMap(Person::getHomeAddress)
                .flatMap(Address::getCity);
    }

    // return String + default value
    public String selectCeoCityByCompanyId1(int companyId) {
        return companyDao.selectById(companyId)
                .map(Company::getCeo)
                .flatMap(Person::getHomeAddress)
                .flatMap(Address::getCity)
                .orElse("UNKNOWN");
    }

    // return String + exception
    public String selectCeoCityByCompanyId2(int companyId) throws NoSuchElementException {
        return companyDao.selectById(companyId)
                .map(Company::getCeo)
                .flatMap(Person::getHomeAddress)
                .flatMap(Address::getCity)
                .orElseThrow(NoSuchElementException::new);
    }
}

interface CompanyDao {
    // real situation: no company for such id -> use Optional<Company> 
    Optional<Company> selectById(int id);
}

class Company {
    // company always has ceo -> use Person 
    Person ceo;
    public Person getCeo() {return ceo;}
}

class Person {
    // person always has name -> use String
    String firstName;
    // person can be without address -> use Optional<Address>
    Optional<Address> homeAddress = Optional.empty();

    public String getFirstName() {return firstName;}   
    public Optional<Address> getHomeAddress() {return homeAddress;}
}

class Address {
    //  address always contains country -> use String
    String country;
    //  city field is optional -> use Optional<String>
    Optional<String> city = Optional.empty();

    String getCountry() {return country;}    
    Optional<String> getCity() {return city;}
}

我高度无视建议在任何情况下使用空对象的答案。这种模式可能会破坏合同,将问题埋得越来越深,而不是解决问题,更不用说使用不当会产生另一堆需要未来维护的样板代码。

实际上,如果从方法返回的某个值可以为空,并且调用代码必须对此做出决定,那么应该有一个更早的调用来确保状态。

还请记住,如果不小心使用,空对象模式将占用内存。为此,NullObject的实例应该在所有者之间共享,而不是每个所有者的unigue实例。

此外,我不建议在类型是原始类型表示的情况下使用这种模式,比如数学实体,它们不是标量:向量、矩阵、复数和POD(普通旧数据)对象,它们是用来以Java内置类型的形式保存状态的。在后一种情况下,您将以任意结果调用getter方法。例如,NullPerson.getName()方法应该返回什么?

为了避免荒谬的结果,值得考虑这样的案例。

!=的另一种选择空检查是(如果你无法在设计上摆脱它):

Optional.ofNullable(someobject).ifPresent(someobject -> someobject.doCalc());

or

Optional.ofNullable(someobject).ifPresent(SomeClass::doCalc);

SomeClass是某个对象的类型。

但是,您无法从doCalc()获取返回值,因此仅对void方法有用。