提问



我使用object != null来避免NullPointerException[159]


有没有一个很好的替代品呢?


例如:


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


这样可以避免NullPointerException,当不知道对象是否null时。


请注意,接受的答案可能已过期,请参阅https://stackoverflow.com/a/2386013/12943以获取更新的方法。

最佳参考


这对我来说听起来像是一个相当普遍的问题,初级到中级开发人员往往会在某些时候面临这样的问题:他们要么不知道,要么不相信他们参与的合同以及防御性地过度检查空值。另外,在编写自己的代码时,它们倾向于依赖返回空值来指示某些东西,从而要求调用者检查空值。


换句话说,有两个实例进行空检查:



  1. 如果null是合同方面的有效回复;和

  2. 不是一个有效的回应。



(2)很容易。使用assert语句(断言)或允许失败(例如,NullPointerException)。断言是1.4中添加的高度未充分利用的Java功能。语法是:[161]


assert <condition>


要么


assert <condition> : <object>


其中<condition>是布尔表达式,<object>是一个对象,其toString()方法的输出将包含在错误中。


如果条件不为真,则assert语句抛出Error(AssertionError)。默认情况下,Java会忽略断言。您可以通过将选项-ea传递给JVM来启用断言。您可以为各个类和包启用和禁用断言。这意味着您可以在开发和测试时使用断言验证代码,并在生产环境中禁用它们,尽管我的测试表明,断言没有性能影响。


在这种情况下不使用断言是可以的,因为代码只会失败,如果使用断言将会发生这种情况。唯一的区别在于断言可能会更快地发生,以更有意义的方式发生,并且可能带有额外的信息,这可能会帮助您弄清楚如果您没有预料到它会发生的原因。


(1)有点难。如果您无法控制您正在调用的代码,那么您就会陷入困境。如果null是有效响应,则必须检查它。


如果它是你控制的代码(然而通常就是这种情况),那么这是一个不同的故事。避免使用空值作为响应。使用返回集合的方法,很容易:几乎一直返回空集合(或数组)而不是null。


对于非收藏,它可能更难。以此为例:如果您有这些接口:


public interface Action {
  void doSomething();
}

public interface Parser {
  Action findAction(String userInput);
}


其中Parser接受原始用户输入并找到要做的事情,也许如果您正在为某些事情实现命令行界面。现在您可以使合同在没有适当的操作时返回null。这导致你正在谈论的空检查。


另一种解决方案是永远不会返回null,而是使用Null对象模式:[162]


public class MyParser implements Parser {
  private static Action DO_NOTHING = new Action() {
    public void doSomething() { /* do nothing */ }
  };

  public Action findAction(String userInput) {
    // ...
    if ( /* we can't find any actions */ ) {
      return DO_NOTHING;
    }
  }
}


比较:


Parser parser = ParserFactory.getParser();
if (parser == null) {
  // now what?
  // this would be an example of where null isn't (or shouldn't be) a valid response
}
Action action = parser.findAction(someInput);
if (action == null) {
  // do nothing
} else {
  action.doSomething();
}





ParserFactory.getParser().findAction(someInput).doSomething();


这是一个更好的设计,因为它导致更简洁的代码。


也就是说,也许findAction()方法完全适合抛出带有意义错误消息的异常 - 特别是在你依赖用户输入的情况下。对于findAction方法来说,抛出一个Exception比使用一个简单的NullPointerException而没有解释的调用方法要好得多。


try {
    ParserFactory.getParser().findAction(someInput).doSomething();
} catch(ActionNotFoundException anfe) {
    userConsole.err(anfe.getMessage());
}


或者,如果您认为try/catch机制太难看,而不是Do Nothing,则默认操作应该向用户提供反馈。


public Action findAction(final String userInput) {
    /* Code to return requested Action if found */
    return new Action() {
        public void doSomething() {
            userConsole.err("Action not found: " + userInput);
        }
    }
}

其它参考1


如果您使用(或计划使用)Java IDE,如JetBrains IntelliJ IDEA,Eclipse或Netbeans或像findbugs这样的工具,那么您可以使用注释来解决此问题。[163]


基本上,你有@Nullable@NotNull


您可以在方法和参数中使用,如下所示:


@NotNull public static String helloWorld() {
    return "Hello World";
}


要么


@Nullable public static String helloWorld() {
    return "Hello World";
}


第二个例子不会编译(在IntelliJ IDEA中)。


在另一段代码中使用第一个helloWorld()函数时:


public static void main(String[] args)
{
    String result = helloWorld();
    if(result != null) {
        System.out.println(result);
    }
}


现在,IntelliJ IDEA编译器会告诉您检查没用,因为helloWorld()函数永远不会返回null


使用参数


void someMethod(@NotNull someParameter) { }


如果你写的东西像:


someMethod(null);


这不会编译。


最后一个例子@Nullable


@Nullable iWantToDestroyEverything() { return null; }


这样做


iWantToDestroyEverything().something();


你可以肯定这不会发生。:)


这是一种很好的方式让编译器检查比平常更多的东西,并强制你的合同变得更强。不幸的是,它并没有得到所有编译器的支持。


在IntelliJ IDEA 10.5及其中,他们增加了对任何其他@Nullable @NotNull实现的支持。


请参阅博客文章更灵活,可配置的@Nullable/@NotNull注释。[164]

其它参考2


如果不允许空值



如果您的方法是外部调用的,请从以下内容开始:


public void method(Object object) {
  if (object == null) {
    throw new IllegalArgumentException("...");
  }


然后,在该方法的其余部分中,您将知道object不为空。


如果它是一个内部方法(不是API的一部分),只需记录它不能为空,那就是它。


例:


public String getFirst3Chars(String text) {
  return text.subString(0, 3);
}


但是,如果您的方法只是传递了值,并且下一个方法将其传递给它,则可能会出现问题。在这种情况下,您可能需要检查上面的参数。


如果允许null



这真的取决于。如果发现我经常做这样的事情:


if (object == null) {
  // something
} else {
  // something else
}


所以我分支,做两件完全不同的事情。没有丑陋的代码片段,因为我真的需要根据数据做两件事。例如,我应该处理输入,还是应该计算一个好的默认值?





我真的很少使用这个成语if (object != null && ...


如果您显示通常使用成语的示例,则可能更容易为您提供示例。

其它参考3


哇,当我们有57种不同的方式推荐NullObject pattern时,我几乎不想添加另一个答案,但我认为有些人对这个问题感兴趣可能想知道Java 7上有一个提议。添加null-safe handling— if-not-equal-null逻辑的简化语法。[165]


Alex Miller给出的例子如下:


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


?.表示如果左标识符不为空则仅取消引用,否则将表达式的其余部分计算为null。有些人,比如Java Posse成员Dick Wall和Devoxx的选民都非常喜欢这个提议,但也存在反对意见,理由是它实际上鼓励更多地使用null作为哨兵价值。[166]]]





更新:已在Project Coin下提交了针对Java 7中的零安全运算符的官方提议。语法与上面的例子略有不同,但它是相同的概念。[167] [168]





更新: null-safe运算符提议并没有成为Project Coin。所以,你不会在Java 7中看到这种语法。

其它参考4


如果不允许未定义的值:



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


如果允许未定义的值:



如果要定义未定义值有意义的新API ,请使用选项模式(可能熟悉函数式语言)。它具有以下优点:[169]



  • 在API中明确说明输入或输出是否存在。

  • 编译器强制您处理未定义的情况。

  • 选项是一个monad,因此不需要详细的null检查,只需使用map/foreach/getOrElse或类似的组合器来安全地使用该值(示例)。



Java 8有一个内置的Optional类(推荐);对于早期版本,有库替代品,例如GuavaOptional或FunctionalJavaOption。但是,与许多功能样式模式一样,在Java中使用Option(甚至8)会产生相当多的样板,使用较简洁的JVM语言可以减少这种样板,例如: Scala或Xtend。[170] [171] [172] [173] [174] [175] [176]


如果你必须处理可能返回null的API ,你就不能用Java做很多事.Xtend和Groovy有Elvis运算符?:和null-safe dereference运算符?.,但请注意,如果是空引用,则返回null,因此它只是推迟对null的正确处理。[177] [178]

其它参考5


仅限于这种情况 -


在调用equals方法之前不检查变量是否为null(下面的字符串比较示例):


if ( foo.equals("bar") ) {
 // ...
}


如果foo不存在,将导致NullPointerException


如果你比较你的String,你可以避免这种情况:


if ( "bar".equals(foo) ) {
 // ...
}

其它参考6


随着Java 8的推出,新的java.util.Optional类可以解决一些问题。至少可以说它提高了代码的可读性,在公共API的情况下,使API的合同更清晰。


他们的工作方式如下:


创建给定类型的可选对象(Fruit)作为方法的返回类型。它可以为空或包含Fruit对象:


public static Optional<Fruit> find(String name, List<Fruit> fruits) {
   for (Fruit fruit : fruits) {
      if (fruit.getName().equals(name)) {
         return Optional.of(fruit);
      }
   }
   return Optional.empty();
}


现在看一下我们搜索给定Fruit实例的Fruit(fruits列表的代码:


Optional<Fruit> found = find("lemon", fruits);
if (found.isPresent()) {
   Fruit fruit = found.get();
   String name = fruit.getName();
}


您可以使用map()运算符对可选对象执行计算或从中提取值。 orElse()允许您提供缺失值的后备。


String nameOrNull = find("lemon", fruits)
    .map(f -> f.getName())
    .orElse("empty-name");


当然,检查null/empty值仍然是必要的,但至少开发人员意识到该值可能是空的并且忘记检查的风险是有限的。


在使用Optional从头构建的API中,只要返回值可能为空,并且仅当它不能为null(约定)时返回普通对象,客户端代码可能会放弃对简单对象返回的空检查值...


当然Optional也可以用作方法参数,在某些情况下,可能是比5或10重载方法更好的指示可选参数的方法。


Optional提供了其他方便的方法,例如orElse允许使用默认值,ifPresent使用lambda表达式。[179]


我邀请你阅读这篇文章(我写这个答案的主要来源),其中NullPointerException(和一般的空指针)问题以及Optional带来的(部分)解决方案得到了很好的解释: Java可选对象。[180]

其它参考7


根据您检查的对象类型,您可以使用apache公共中的一些类,例如:apache commons lang和apache commons collections [181] [182]


例:


String foo;
...
if( StringUtils.isBlank( foo ) ) {
   ///do something
}


或(取决于您需要检查的内容):


String foo;
...
if( StringUtils.isEmpty( foo ) ) {
   ///do something
}


StringUtils类只是其中之一;在公共场所中有很多优秀的类可以安全操作。


下面是一个如何在包含apache库(commons-lang-2.4.jar)时在JAVA中使用null vallidation的示例


public DOCUMENT read(String xml, ValidationEventHandler validationEventHandler) {
    Validate.notNull(validationEventHandler,"ValidationHandler not Injected");
    return read(new StringReader(xml), true, validationEventHandler);
}


如果您使用的是Spring,Spring在其包中也具有相同的功能,请参阅library(spring-2.4.6.jar)


有关如何从spring使用此静态classf的示例(org.springframework.util.Assert)


Assert.notNull(validationEventHandler,"ValidationHandler not Injected");

其它参考8



  • 如果您认为某个对象不应为null(或者它是一个错误),请使用断言。

  • 如果你的方法不接受null params在javadoc中说它并使用断言。



只有当你想处理对象可能为null的情况时,你必须检查对象!= null ...


有人建议在Java7中添加新的注释来帮助null/notnull参数:
http://tech.puredanger.com/java7/#jsr308[183]

其它参考9


我是快速失败代码的粉丝。问问自己 - 在参数为空的情况下,你是否正在做一些有用的事情?如果你没有明确答案你的代码在那种情况下应该做什么...... I.e。它首先应该永远不为null,然后忽略它并允许抛出NullPointerException。调用代码将与IllegalArgumentException一样具有NPE意义,但是对于开发人员来说,如果抛出NPE而不是代码试图执行其他意外的意外事件,它将更容易调试和理解出错的地方。逻辑 - 最终导致应用程序失败。

其它参考10


有时,您有一些方法可以对其参数进行操作,从而定义对称操作:


a.f(b); <-> b.f(a);


如果你知道b永远不能为空,你可以交换它。它对equals最有用:
而不是foo.equals("bar");更好"bar".equals(foo);

其它参考11


Google集合框架提供了一种实现空检查的优雅方式。


像这样的库类中有一个方法:


static <T> T checkNotNull(T e) {
   if (e == null) {
      throw new NullPointerException();
   }
   return e;
}


用法是(import static):


...
void foo(int a, Person p) {
   if (checkNotNull(p).getAge() > a) {
      ...
   }
   else {
      ...
   }
}
...


或者在你的例子中:


checkNotNull(someobject).doCalc();

其它参考12


而不是Null对象模式 - 它有其用途 - 你可能会考虑null对象是一个bug的情况。


抛出异常时,检查堆栈跟踪并完成错误。

其它参考13


Java 7有一个新的java.util.Objects实用程序类,其上有requireNonNull()方法。如果它的参数为null,所有这一切都会抛出NullPointerException,但它会稍微清理一下代码。例:


Objects.requireNonNull(someObject);
someObject.doCalc();





该方法对于在构造函数中的赋值之前进行检查非常有用,其中每次使用它都可以保存三行代码:


Parent(Child child) {
   if (child == null) {
      throw new NullPointerException("child");
   }
   this.child = child;
}





Parent(Child child) {
   this.child = Objects.requireNonNull(child, "child");
}

其它参考14


Null不是问题。它是完整建模工具集的组成部分。软件旨在模拟世界的复杂性,而null则承担其负担。 Null表示Java等中的无数据或未知。因此,为这些目的使用null是合适的。我不喜欢Null对象模式;我认为它会提升谁会守护
监护人的问题
如果你问我女朋友的名字我会告诉你我没有女朋友。在Java语言中,我将返回null。
另一种方法是抛出有意义的异常来指示一些不能(或者不希望)在那里解决的问题并将其委托给堆栈中更高的位置来重试或向用户报告数据访问错误。 [185] [186]



  1. 对于未知问题,请提供未知答案。(从业务角度来看,这是正确无效的)在使用之前在方法内检查一次null参数可以减少多个调用者在通话前检查它们。


    public Photo getPhotoOfThePerson(Person person) {
        if (person == null)
            return null;
        // Grabbing some resources or intensive calculation
        // using person object anyhow.
    }
    


    上一步导致正常的逻辑流程,从我的照片库中得不到任何不存在的女朋友的照片。


    getPhotoOfThePerson(me.getGirlfriend())
    


    它适合新的Java API(期待)


    getPhotoByName(me.getGirlfriend()?.getName())
    


    虽然对于某些人来说,不是为了找到存储在数据库中的照片而是正常的业务流程,但我常常使用如下所示的对来用于其他一些情况


    public static MyEnum parseMyEnum(String value); // throws IllegalArgumentException
    public static MyEnum parseMyEnumOrNull(String value);
    


    并且不厌恶输入<alt> + <shift> + <j>(在Eclipse中生成javadoc)并为您的公共API写下三个额外的单词。这对于除了那些不阅读文档的人来说已经足够了。


    /**
     * @return photo or null
     */
    


    要么


    /**
     * @return photo, never null
     */
    

  2. 这是理论上的情况,在大多数情况下,您应该更喜欢java null safe API(如果它将在另外10年内发布),但NullPointerExceptionException的子类。因此它是Throwable的一种形式,表示合理的应用程序可能想要捕获的条件(javadoc)!为了使用异常的第一个最大优势和从常规代码中分离错误处理代码(根据Java的创建者),对我来说,抓住NullPointerException [187] [188]是合适的。]]


    public Photo getGirlfriendPhoto() {
        try {
            return appContext.getPhotoDataSource().getPhotoByName(me.getGirlfriend().getName());
        } catch (NullPointerException e) {
            return null;
        }
    }
    


    可能会出现问题:


    问:如果getPhotoDataSource()返回null,该怎么办?
    答:这取决于业务逻辑。如果我找不到相册,我将向你展示没有照片。如果没有初始化appContext怎么办?这个方法的业务逻辑就是这样。如果相同的逻辑应该更严格,那么抛出异常它就是业务逻辑的一部分,应该使用null的显式检查(情况3)。 新的Java Null-safe API在这里更适合指定有意义的含义和不暗示初始化的内容在程序员错误的情况下快速失败。


    问:可以执行冗余代码并且可以获取不必要的资源
    A.如果getPhotoByName()尝试打开数据库连接,创建PreparedStatement并最后使用人名作为SQL参数,则可能发生。针对未知问题的方法提供了一个未知的答案(案例1)在这里工作。在获取资源之前,该方法应检查参数并在需要时返回未知结果。


    问:由于尝试关闭开放,这种方法会带来性能损失
    答:首先应该易于理解和修改软件。只有在此之后,才能考虑性能,只有在需要时!并在需要的地方! (来源)等等。[189]


    PS。这种方法使用起来是合理的,因为单独的错误处理代码来自常规代码原则在某些地方使用是合理的。考虑下一个例子:


    public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) {
        try {
            Result1 result1 = performSomeCalculation(predicate);
            Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());
            Result3 result3 = performThirdCalculation(result2.getSomeProperty());
            Result4 result4 = performLastCalculation(result3.getSomeProperty());
            return result4.getSomeProperty();
        } catch (NullPointerException e) {
            return null;
        }
    }
    
    public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) {
        SomeValue result = null;
        if (predicate != null) {
            Result1 result1 = performSomeCalculation(predicate);
            if (result1 != null && result1.getSomeProperty() != null) {
                Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());
                if (result2 != null && result2.getSomeProperty() != null) {
                    Result3 result3 = performThirdCalculation(result2.getSomeProperty());
                    if (result3 != null && result3.getSomeProperty() != null) {
                        Result4 result4 = performLastCalculation(result3.getSomeProperty());
                        if (result4 != null) {
                            result = result4.getSomeProperty();
                        }
                    }
                }
            }
        }
        return result;
    }
    


    PPS。对于那些快速投票(而不是那么快阅读文档)的人,我想说我的生活中从来没有遇到空指针异常(NPE)。但这种可能性故意设计由Java创建者,因为NPE是Exception的子类。当ThreadDeathError时,我们在Java历史中有一个先例,不是因为它实际上是一个应用程序错误,而仅仅因为它是不打算被抓住!NPE适合Error多于ThreadDeath!但事实并非如此。

  3. 仅在业务逻辑暗示时才检查无数据。


    public void updatePersonPhoneNumber(Long personId, String phoneNumber) {
        if (personId == null)
            return;
        DataSource dataSource = appContext.getStuffDataSource();
        Person person = dataSource.getPersonById(personId);
        if (person != null) {
            person.setPhoneNumber(phoneNumber);
            dataSource.updatePerson(person);
        } else {
            Person = new Person(personId);
            person.setPhoneNumber(phoneNumber);
            dataSource.insertPerson(person);
        }
    }
    





    public void updatePersonPhoneNumber(Long personId, String phoneNumber) {
        if (personId == null)
            return;
        DataSource dataSource = appContext.getStuffDataSource();
        Person person = dataSource.getPersonById(personId);
        if (person == null)
            throw new SomeReasonableUserException("What are you thinking about ???");
        person.setPhoneNumber(phoneNumber);
        dataSource.updatePerson(person);
    }
    


    如果未初始化appContext或dataSource,则未处理的运行时NullPointerException将终止当前线程并将由Thread.defaultUncaughtExceptionHandler处理(您可以定义和使用您喜欢的记录器或其他通知机制)。如果未设置,ThreadGroup#uncaughtException将stacktrace打印到系统错误。应该监视应用程序错误日志并为每个未处理的异常打开Jira问题,这实际上是应用程序错误。程序员应该在初始化的东西中修复bug。[190] [191]


其它参考15


最终,完全解决此问题的唯一方法是使用不同的编程语言:



  • 在Objective-C中,您可以在nil上执行相当于调用方法的操作,绝对不会发生任何事情。这使得大多数空检查变得不必要,但它可以使错误更难以诊断。

  • 在尼斯,一种Java派生语言中,所有类型都有两个版本:潜在空版本和非空版本。您只能在非null类型上调用方法。通过显式检查null,可以将可能为空的类型转换为非null类型。这使得更容易知道需要进行空检查的位置以及它们不是t。
  • 的位置

其它参考16


Java中常见的问题。[192]


首先,我对此的看法:


我认为当NULL传递时吃某些东西是不好的,其中NULL不是一个有效的值。如果你没有以某种错误退出该方法,那么这意味着你的方法没有出错,这是不正确的。然后你可能在这种情况下返回null,并且在接收方法中你再次检查null,它永远不会结束,你最终得到if!= null等。


因此,IMHO,null必须是一个严重的错误,它会阻止进一步执行(即null不是有效值)。


我解决这个问题的方法是这样的:


首先,我遵循这个惯例:



  1. 所有公共方法/API始终检查其参数为null

  2. 所有私有方法都不检查null,因为它们是受控方法(只是在没有处理上面的情况下使用null指针异常)

  3. 唯一不检查null的其他方法是实用程序方法。它们是公开的,但如果你出于某种原因打电话给它们,你就知道你传递了什么参数。这就像试图在没有提供水的情况下在水壶中煮水......



最后,在代码中,public方法的第一行如下:


ValidationUtils.getNullValidator().addParam(plans, "plans").addParam(persons, "persons").validate();


请注意,addParam()返回s self,以便您可以添加更多参数进行检查。


方法validate()将抛出checked ValidationException,如果任何参数为null(选中或取消选中更多是设计/品味问题,但我的ValidationException被选中)。


void validate() throws ValidationException;


例如,如果plans为null,则该消息将包含以下文本:


参数[[计划]] 遇到非法参数值null


正如您所看到的,用户消息需要addParam()方法(字符串)中的第二个值,因为您无法轻松检测传入的变量名称,即使使用反射(无论如何都不是此帖子的主题......)。


是的,我们知道超出这一行我们将不再遇到空值,所以我们只是安全地调用这些对象上的方法。


这样,代码就干净,易于维护和读取。

其它参考17


问这个问题指出你可能对错误处理策略感兴趣。您的团队的架构师应该决定如何处理错误。有几种方法可以做到这一点:



  1. 允许异常通过 - 在主循环或其他一些管理例程中捕获它们。



    • 检查错误情况并妥善处理




当然也要看看面向方面编程 - 他们有很好的方法将if( o == null ) handleNull()插入到你的字节码中。

其它参考18


除了使用assert之外,您还可以使用以下内容:


if (someobject == null) {
    // Handle null here then move on.
}


这略好于:


if (someobject != null) {
    .....
    .....



    .....
}

其它参考19


只是不要使用null。不要允许它。


在我的类中,大多数字段和局部变量都具有非空的默认值,并且我在代码中的任何位置添加了契约语句(always-on asserts)以确保执行此操作(因为它更简洁,更具表现力)让它作为NPE出现,然后必须解决行号等)。


一旦我采用了这种做法,我注意到问题似乎已经解决了。你只是偶然地在开发过程中抓住了一些事情并意识到你有一个弱点......更重要的是......它有助于封装不同的模块关注点,不同的模块可以相互信任,而且不再乱扔垃圾代码if = null else构造!


这是防御性编程,从长远来看会产生更清晰的代码。始终清理数据,例如在这里强制执行严格的标准,问题就消失了。


class C {
    private final MyType mustBeSet;
    public C(MyType mything) {
       mustBeSet=Contract.notNull(mything);
    }
   private String name = "<unknown>";
   public void setName(String s) {
      name = Contract.notNull(s);
   }
}


class Contract {
    public static <T> T notNull(T t) { if (t == null) { throw new ContractException("argument must be non-null"); return t; }
}


合同就像迷你单元测试一样,即使在生产中也一直在运行,当事情失败时,你知道为什么,而不是随机的NPE,你必须以某种方式弄清楚。

其它参考20


Guava是Google非常有用的核心库,它有一个很好的有用API来避免空值。我发现UsingAndAvoidingNullExplained非常有帮助。[193]


如维基中所述:



  Optional<T>是用a替换可空的T引用的一种方法
  非空值。 Optional可以包含非空T引用
  (在这种情况下,我们说引用是存在),或者它可能包含
  没有(在这种情况下,我们说引用是缺席)。永远不会
  据说包含空。



用法:


Optional<Integer> possible = Optional.of(5);
possible.isPresent(); // returns true
possible.get(); // returns 5

其它参考21


这对每个Java开发人员来说都是一个非常普遍的问题所以在Java  8中有官方支持来解决这些问题而没有混乱的代码。


Java 8引入了java.util.Optional<T>。它是一个容器,可能包含也可能不包含非null值。 Java 8提供了一种更安全的方法来处理在某些情况下其值可能为null的对象。它的灵感来自Haskell和Scala的想法。[194] [195]


简而言之,Optional类包括显式处理值存在或不存在的情况的方法。但是,与null引用相比的优点是Optional< T>类强制您在值不存在时考虑这种情况。因此,您可以防止意外的空指针异常。


在上面的示例中,我们有一个家庭服务工厂,它返回家中可用的多个设备的句柄。但这些服务可能有效,也可能无效;这意味着它可能导致NullPointerException。不要在使用任何服务之前添加null if条件,而是将其包装到Optional< Service>中。


包装选项< T>


让我们考虑一种从工厂获取服务引用的方法。而不是返回服务引用,用Optional包装它。它让API用户知道返回的服务可能可用或不可用/使用,防御性地使用


public Optional<Service> getRefrigertorControl() {
      Service s = new  RefrigeratorService();
       //...
      return Optional.ofNullable(s);
   }


如您所见Optional.ofNullable()提供了一种简单的方法来获取引用。还有另一种方法可以获得Optional的引用,Optional.empty()和[[Optional.of()。一个用于返回空对象而不是重新调整null,另一个用于包装不可为空的对象。


那么它如何帮助避免空检查?


一旦包装了引用对象,Optional就会提供许多有用的方法来在没有NPE的情况下调用包装引用上的方法。


Optional ref = homeServices.getRefrigertorControl();
ref.ifPresent(HomeServices::switchItOn);


Optional.ifPresent如果它是非空值,则使用引用调用给定的Consumer。否则,它什么都不做。


@FunctionalInterface
public interface Consumer<T>


表示接受单个输入参数且不返回结果的操作。与大多数其他功能接口不同,消费者需要通过副作用进行操作。
它非常干净,易于理解。在上面的代码示例中,如果Optional保持引用为非null,则会调用HomeService.switchOn(Service)


我们经常使用三元运算符来检查空条件并返回替代值或默认值。 Optional提供了另一种处理相同条件的方法,而不检查null。如果Optional具有空值,则Optional.orElse(defaultObj)返回defaultObj。让我们在示例代码中使用它:


public static Optional<HomeServices> get() {
    service = Optional.of(service.orElse(new HomeServices()));
    return service;
}


现在HomeServices.get()做了同样的事情,但是以更好的方式。它检查服务是否已初始化。如果是,则返回相同或创建新的新服务。可选< T> .orElse(T)有助于返回默认值。


最后,这是我们的NPE以及无空检查代码:


import java.util.Optional;
public class HomeServices {
    private static final int NOW = 0;
    private static Optional<HomeServices> service;

public static Optional<HomeServices> get() {
    service = Optional.of(service.orElse(new HomeServices()));
    return service;
}

public Optional<Service> getRefrigertorControl() {
    Service s = new  RefrigeratorService();
    //...
    return Optional.ofNullable(s);
}

public static void main(String[] args) {
    /* Get Home Services handle */
    Optional<HomeServices> homeServices = HomeServices.get();
    if(homeServices != null) {
        Optional<Service> refrigertorControl = homeServices.get().getRefrigertorControl();
        refrigertorControl.ifPresent(HomeServices::switchItOn);
    }
}

public static void switchItOn(Service s){
         //...
    }
}


完整的帖子是 NPE以及Null免检代码......真的吗?。[196]

其它参考22


我喜欢Nat Pryce的文章。以下是链接:



  • 避免多态调度的空白

  • 用告诉,不要避免无效问风格



在文章中还有一个指向Java Maybe Type的Git存储库的链接,我发现它很有趣,但我不认为它可以减少
检查代码臃肿。在对互联网进行一些研究之后,我认为!= null 代码膨胀可能主要通过精心设计来减少。[197] [198]

其它参考23


我已经尝试了NullObjectPattern,但对我来说并不总是最好的方式。有时候没有行动是不合适的。


NullPointerException是一个运行时异常,这意味着它是开发人员的错误,并且有足够的经验它会告诉您确切的错误位置。


现在回答:


尽量使所有属性及其访问者尽可能保密,或者避免将它们暴露给客户端。当然,你可以在构造函数中包含参数值,但是通过减小范围,你不要让客户端类传递无效值。如果你需要修改值,你总是可以创建一个新的object。您只检查构造函数中的值一次,在其他方法中,您几乎可以确定这些值不为空。


当然,经验是理解和应用此建议的更好方式。


字节!

其它参考24


Java 8或更高版本的最佳替代方案可能是使用Optional类。 [199]


Optional stringToUse = Optional.of("optional is there");
stringToUse.ifPresent(System.out::println);


对于可能为空的值的长链,这尤其方便。例:


Optional<Integer> i = Optional.ofNullable(wsObject.getFoo())
    .map(f -> f.getBar())
    .map(b -> b.getBaz())
    .map(b -> b.getInt());


有关如何在null上抛出异常的示例:


Optional optionalCarNull = Optional.ofNullable(someNull);
optionalCarNull.orElseThrow(IllegalStateException::new);


Java 7引入了Objects.requireNonNull方法,当检查某些内容是否为非null时,该方法很方便。例如:[200]


String lowerVal = Objects.requireNonNull(someVar, "input cannot be null or empty").toLowerCase();

其它参考25


我可以更一般地回答它!


当方法以我们不期望的方式获取参数时,我们通常面对这个问题(错误的方法调用是程序员的错误)。例如:你希望得到一个对象,而不是你得到一个你希望得到一个至少包含一个字符的字符串,而不是你得到一个空字符串......


所以两者之间没有区别:


if(object == null){
   //you called my method badly!


}


要么


if(str.length() == 0){
   //you called my method badly again!
}


在我们执行任何其他功能之前,他们都希望确保我们收到有效参数。


如其他一些答案所述,为了避免上述问题,您可以按照按合同设计模式进行操作。请参阅http://en.wikipedia.org/wiki/Design_by_contract。 [201]


要在java中实现此模式,您可以使用核心java注释,如 javax.annotation.NotNull ,或使用更复杂的库,如 Hibernate Validator


只是一个样本:


getCustomerAccounts(@NotEmpty String customerId,@Size(min = 1) String accountType)


现在,您可以安全地开发方法的核心功能,而无需检查输入参数,它们可以保护您的方法免受意外参数的影响。


您可以更进一步,确保只能在您的应用程序中创建有效的pojos。 (来自hibernate验证器站点的示例)


public class Car {

   @NotNull
   private String manufacturer;

   @NotNull
   @Size(min = 2, max = 14)
   private String licensePlate;

   @Min(2)
   private int seatCount;

   // ...
}

其它参考26


我高度忽略了建议在每种情况下使用null对象的答案。这种模式可能会破坏合同并将问题深入和深入地埋没而不是解决它们,没有提到使用不当会产生另一堆样板代码,这将需要将来的维护。


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


还要记住,如果毫无顾虑地使用空对象模式将是内存饥饿。为此 - 应该在所有者之间共享NullObject的实例,而不是每个实例的unigue实例。


此外,我不建议使用此模式,其中类型是基本类型表示 - 如数学实体,不是标量:向量,矩阵,复数和POD(普通旧数据)对象,它们旨在保持状态以Java内置类型的形式。在后一种情况下,您最终会调用具有任意结果的getter方法。例如,NullPerson.getName()方法应返回什么?


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

其它参考27



  1. 永远不要将变量初始化为null。

  2. 如果(1)不可用,请将所有集合和数组初始化为空集合/数组。



在您自己的代码中执行此操作,您可以避免!= null检查。


大多数情况下,null检查似乎保护集合或数组的循环,所以只需将它们初始化为空,就不需要任何空检查。


// Bad
ArrayList<String> lemmings;
String[] names;

void checkLemmings() {
    if (lemmings != null) for(lemming: lemmings) {
        // do something
    }
}



// Good
ArrayList<String> lemmings = new ArrayList<String>();
String[] names = {};

void checkLemmings() {
    for(lemming: lemmings) {
        // do something
    }
}


这有一个很小的开销,但是对于更干净的代码和更少的NullPointerExceptions来说它是值得的。

其它参考28


这是大多数开发人员最常出现的错误。


我们有很多方法可以解决这个问题。


方法1:


org.apache.commons.lang.Validate //using apache framework


notNull(Object object,String message)


方法2:


if(someObject!=null){ // simply checking against null
}


方法3:


@isNull @Nullable  // using annotation based validation


方法4:


// by writing static method and calling it across whereever we needed to check the validation

static <T> T isNull(someObject e){  
   if(e == null){
      throw new NullPointerException();
   }
   return e;
}

其它参考29


public static <T> T ifNull(T toCheck, T ifNull) {
    if (toCheck == null) {
           return ifNull;
    }
    return toCheck;
}