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

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

当前回答

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

其他回答

Java8在Java.util包中引入了一个新的类Optional。它用于表示值是否存在。这种新构造的主要优点是不再有太多的空检查和NullPointerException。它避免了任何运行时NullPointerExceptions,并支持我们开发干净整洁的Java API或应用程序。与集合和数组一样,它也是一个最多只能容纳一个值的容器。

下面是一些有用的链接,您可以关注

https://www.mkyong.com/java8/java-8-optional-in-depth/

https://dzone.com/articles/java-8-optional-avoid-null-and

在Java中避免空检查的最佳方法是正确处理和使用异常。在我的经验中,随着您向前端移动,空检查变得越来越常见和必要,因为它更接近于通过UI提供无效信息的用户(例如,没有值,为字段提交)。

有人可能会争辩说,你应该能够控制UI正在做什么,以免你忘记大多数UI是通过某种类型的第三方库完成的,例如,根据情况或库的不同,它可能会为空白文本框返回NULL或空字符串。

您可以这样组合这两者:

try
{
  myvar = get_user_supplied_value(); 
  if (myvar == null || myvar.length() == 0) { alert_the_user_somehow(); return; };

  process_user_input(myvar);
} catch (Exception ex) {
  handle_exception(ex);
}

人们采取的另一种方法是:

if (myvar && myvar.length() > 0)  { };

你也可以抛出一个异常(这是我更喜欢的)

if (myvar == null || myvar.length() == 0) {
 throw new Exception("You must supply a name!");
};

但这取决于你。

避免不必要的空检查的方法很简单:

您需要知道哪些变量可以为空,哪些变量不能为空,并且您需要对给定变量属于哪一类有信心。

但是,尽管它可以说得很简单,但实现它却很困难。关键在于自信的部分,因为你如何确定变量不能为空?

对此没有快速的解决方法,但这里有一些提示:

干净的代码。能够推理一段代码的行为最重要的一点是,它是以易于理解的方式编写的。根据变量所代表的内容命名变量,根据它们所做的内容命名方法,应用单一责任原则(SOLID中的S:http://en.wikipedia.org/wiki/SOLID_(object-oriented_design),这意味着每一段代码都应该有一个单独的责任,并且不做任何其他事情)。一旦你的代码是干净的,那么你就更容易理解它,也更容易理解多个代码层。对于杂乱的代码,试图理解一个方法的作用可能会让你忘记当初为什么要读这个方法。(提示:阅读罗伯特·C·马丁的《清洁代码》)避免返回空值。如果空值会使程序无法正常运行,请改为抛出异常(确保添加适当的错误处理)。返回空值可能是可接受的情况,例如,尝试从数据库中获取对象。在这些情况下,编写处理空值的代码,并在您的耳朵后面记下,这里有可能返回空值的内容。处理返回的null值,使其尽可能接近返回null的方法的调用方(不要盲目地将其传递回调用链)永远不要将显式空值作为参数传递(至少不要跨类传递)。如果您曾经处于传递空参数是唯一选项的位置,那么创建一个不包含此参数的新方法是一种方法。验证您的输入!确定应用程序的“入口点”。它们可以从Web服务、REST服务、远程EJB类、控制器等所有内容。对于这些入口点中的每个方法,问问自己:“如果此参数为空,此方法是否正确执行?”如果答案为否,请添加Validate.notNull(someParam,“当someParam为空时无法运行!”);。如果缺少必需的参数,这将引发IllegalArgumentException。在入口点进行这种类型的验证的好处是,您可以很容易地在从入口点执行的代码中假设该变量永远不会为空!此外,如果这失败了,在入口点,调试会比代码深处出现NullPointerException要容易得多,因为这样的失败只能意味着一件事:客户端没有向您发送所有必需的信息。在大多数情况下,您希望验证所有输入参数,如果您发现自己处于需要允许大量空值的位置,这可能是接口设计不良的标志,需要进行重构/添加以满足客户机的需要。使用集合时,返回一个空集合而不是空集合!使用数据库时,请使用非空约束。这样,您就知道从数据库中读取的值不能为空,并且不必检查它。构造代码并坚持执行。这样做可以让您对代码的行为做出假设,例如,如果应用程序的所有输入都经过验证,那么您可以假设这些值永远不会为空。如果您还没有这样做,请编写代码的自动测试。通过编写测试,您将对代码进行推理,并且您也将更加确信它确实做到了它应该做的事情。此外,自动化测试通过让您立即知道这段代码没有做以前的事情来防止重构过程中的错误。

当然,您仍然需要进行空检查,但它可以降到最低限度(即,知道您可能会得到空值,而不是到处都是空值)。当谈到空检查时,我实际上更喜欢使用三元运算符(但要小心使用,当您开始嵌套它们时,它们会变得非常混乱)

public String nullSafeToString(final Object o) {
    return o != null ? o.toString() : "null";
}

空对象模式可以用作此问题的解决方案。为此,应该修改someObject的类。

public abstract class SomeObject {
   public abstract boolean isNil();
}

public class NullObject extends SomeObject {
   @Override
   public boolean isNil() {
      return true;
   }
}
public class RealObject extends SomeObject {
   @Override
   public boolean isNil() {
      return false;
   }
}

现在先检查一下,

 if (someobject != null) {
    someobject.doCalc();
}

我们可以使用,

if (!someObject.isNil()) {
   someobject.doCalc();
}

参考:https://www.tutorialspoint.com/design_pattern/null_object_pattern.htm

我遵循以下准则以避免空检查。

尽可能避免成员变量的延迟初始化。初始化声明本身中的变量。这将处理NullPointerExceptions。在周期早期决定成员变量的可变性。有效地使用像final关键字这样的语言结构。如果您知道不会更改方法的扩充,请将它们声明为final。尽可能限制数据的变异。某些变量可以在构造函数中创建,并且永远无法更改。删除公共setter方法,除非确实需要它们。例如,假设应用程序中的一个类(A.java)正在维护HashMap这样的集合。不要在A.java中提供公共getter方法,而允许B.java直接在Map中添加元素。而是在A.java中提供一个API,它将一个元素添加到集合中。//避免a.getMap().put(键,值)//推荐的public void addElement(对象键,对象值){//在此处对键和值进行空检查:单个位置map.put(键,值);}最后,在正确的位置有效地使用try{}catch{}finally{}块。