【问题标题】:Best way to check for null values in Java?在 Java 中检查空值的最佳方法是什么?
【发布时间】:2013-06-22 12:58:33
【问题描述】:

在调用对象的函数之前,我需要检查对象是否为空,避免抛出NullPointerException

解决此问题的最佳方法是什么?我考虑过这些方法。
哪一个是 Java 的最佳编程实践?

// Method 1
if (foo != null) {
    if (foo.bar()) {
        etc...
    }
}

// Method 2
if (foo != null ? foo.bar() : false) {
    etc...
}

// Method 3
try {
    if (foo.bar()) {
        etc...
    }
} catch (NullPointerException e) {
}

// Method 4 -- Would this work, or would it still call foo.bar()?
if (foo != null && foo.bar()) {
    etc...
}

【问题讨论】:

  • 永远不要捕获空指针异常。它属于“愚蠢的例外”类别blogs.msdn.com/b/ericlippert/archive/2008/09/10/…
  • 根据您的用例,如果将 null foo 传递给您的方法,则禁用 foonull 值并抛出 NullPointerException 也是有意义的。
  • 1) 1 和 4 之间的区别是样式选择和微优化。 2)只要你不让异常被抛出,这并不重要,而不是担心哪个最好用,你的时间最好花在清晰的设计/更好的算法上。
  • @assylias 在这种情况下应该使用IllegalArgumentException
  • @NickFreeman 我不同意 - 参见例如:stackoverflow.com/a/8196334/829571 - 标准(在 JDK、番石榴和根据 Effective Java 中)是抛出 NPE。虽然 IAE 诚然也常用于这种情况。

标签: java null nullpointerexception


【解决方案1】:

Java 中处理 Null 值的通用方法

<script src="https://gist.github.com/rcvaram/f1a1b89193baa1de39121386d5f865bc.js"></script>
  1. 如果该对象不为空,我们将执行以下操作。

    一个。我们可以改变对象(I)

    b.我们可以返回一些东西(O)作为输出,而不是改变对象(I)

    c。我们可以两者都做

在这种情况下,我们需要传递一个函数,该函数需要获取作为我们的对象的输入 param(I) 如果我们这样接受,那么我们可以根据需要改变该对象。而且那个函数也可能是某物(O)。

  1. 如果object为null,那么我们将做以下事情

    一个。我们可能会以自定义的方式抛出异常

    b.我们可能会返回一些东西。

在这种情况下,对象为空,因此我们需要提供该值,否则我们可能需要抛出异常。

我举两个例子。

  1. 如果我想在字符串中执行修剪,那么该字符串不应为空。在这种情况下,我们必须额外检查 null 值,否则我们将得到 NullPointerException
public String trimValue(String s){
   return s == null ? null : s.trim();
}
  1. 另一个函数,如果该对象不为空,我想为该对象设置一个新值,否则我想抛出一个运行时异常。
public void setTeacherAge(Teacher teacher, int age){
   if (teacher != null){
      teacher.setAge(age);
   } else{
      throw new RuntimeException("teacher is null")
    }
}

根据我的解释,我创建了一个通用方法,它接受值(值可能为 null),一个在对象不为 null 时执行的函数,以及另一个在对象为 null 时执行的供应商函数。

通用函数

  public <I, O> O setNullCheckExecutor(I value, Function<I, O> nonNullExecutor, Supplier<O> nullExecutor) {
        return value != null ? nonNullExecutor.apply(value) : nullExecutor.get();
    }

所以有了这个泛型函数之后,我们可以对示例方法进行如下操作 1.

//To Trim a value
        String trimmedValue = setNullCheckExecutor(value, String::trim, () -> null);

这里,nonNullExecutor 函数是修剪值(使用方法引用)。 nullExecutorFunction is 将返回 null,因为它是一个标识函数。

2.

// mutate the object if not null otherwise throw a custom message runtime exception instead of NullPointerException
 setNullCheckExecutor(teacher, teacher -> {
            teacher.setAge(19);
            return null;
        }, () -> {
            throw new RuntimeException("Teacher is null");
        });

【讨论】:

    【解决方案2】:
    public <T, U> U defaultGet(T supplier, Function<T, U> mapper, U defaultValue) {
            return Optional.ofNullable(supplier).map(mapper).orElse(defaultValue);
    
        }
    

    如果你喜欢函数编程,你可以创建这个函数

    【讨论】:

    • 欢迎来到 StackOverflow,如果您添加一些额外的代码 sn-ps 展示您的建议会很好,这将有助于寻求答案的人从您的答案中获得更多见解
    【解决方案3】:

    对于 java 11+,您可以使用 Objects.nonNull(Object obj)

    if(nonNull(foo)){ 
    //
    }
    

    【讨论】:

      【解决方案4】:

      方法 4 最好。

      if(foo != null && foo.bar()) {
         someStuff();
      }
      

      将使用short-circuit evaluation,这意味着如果logical AND 的第一个条件为假,则结束。

      【讨论】:

      • 谢谢,我认为这是最好的,但我不知道为什么它不会调用第二个条件,或者有时是否可能 - 感谢您解释原因。
      • 是的,因为您使用的是短路操作数 && 所以它不会完成整个表达式
      • @Arty-fishL 这总是一个好问题,因为它依赖于语言 - 通常 类似 C 的语言倾向于从左到右短路,但事实并非如此所有语言的情况,最好总是先检查;)
      【解决方案5】:

      如果您要使用双等号“==”进行检查,则使用对象引用检查 null,例如

      if(null == obj) 
      

      而不是

      if(obj == null)
      

      因为如果你打错了 single equal if(obj = null) 它将返回 true(分配对象返回成功(其值为 'true')。

      【讨论】:

      • (obj = null) 不会评估为真。
      • 此建议仅适用于 C/C++,不适用于 Java。事实上,Java 的建议恰恰相反
      【解决方案6】:

      您也可以使用StringUtils.isNoneEmpty("") 来检查是否为空或为空。

      【讨论】:

        【解决方案7】:

        简单的一行代码来检查是否为空:

        namVar == null ? codTdoForNul() : codTdoForFul();
        

        【讨论】:

          【解决方案8】:

          我们可以使用 Object 类的 Object.requireNonNull 静态方法。实现如下

          public void someMethod(SomeClass obj) {
              Objects.requireNonNull(obj, "Validation error, obj cannot be null");
          }
          

          【讨论】:

            【解决方案9】:

            如果您无权访问 commons apache 库,以下可能会正常工作

            if(null != foo && foo.bar()) {
            //do something
            }
            

            【讨论】:

            • 第二个 sn-p 在 foo 为空时抛出 NPE。
            • Xaerxess,你说得对,我用可能的 NPE 删除了代码 sn-p
            • if you do not have an access to the commons apache library你指的是图书馆的哪一部分?
            【解决方案10】:

            正如其他人所说,#4 是不使用库方法时的最佳方法。但是,您应该始终将 null 放在比较的左侧,以确保在出现拼写错误时不会意外地将 null 分配给 foo。在这种情况下,编译器会发现错误。

            // You meant to do this
            if(foo != null){
            
            // But you made a typo like this which will always evaluate to true
            if(foo = null)
            
            // Do the comparison in this way
            if(null != foo)
            
            // So if you make the mistake in this way the compiler will catch it
            if(null = foo){
            
            // obviously the typo is less obvious when doing an equality comparison but it's a good habit either way
            if(foo == null){
            if(foo =  null){
            

            【讨论】:

            • (foo = null) 不评估为真。
            【解决方案11】:

            Java 7 中,您可以使用 Objects.requireNonNull()。 从java.util 添加Objects 类的导入。

            public class FooClass {
                //...
                public void acceptFoo(Foo obj) {
                    //If obj is null, NPE is thrown
                    Objects.requireNonNull(obj).bar(); //or better requireNonNull(obj, "obj is null");
                }
                //...
            }
            

            【讨论】:

            • 不知何故不允许我将import java.util.Objects; 行添加到代码中,因此请在此处注释。
            • 如果是 Java 8,您可以尝试使用 Optional 类。
            • 如果为 Null 则会抛出一个 NullPointer
            【解决方案12】:

            如果您控制正在调用的 API,请考虑使用 Guava's Optional class

            更多信息here。更改您的方法以返回 Optional&lt;Boolean&gt; 而不是 Boolean

            这会通知调用代码它必须通过调用Optional 中的方便方法之一来考虑 null 的可能性

            【讨论】:

              【解决方案13】:

              您的最后一个建议是最好的。

              if (foo != null && foo.bar()) {
                  etc...
              }
              

              因为:

              1. 更容易阅读。
              2. 这是安全的:如果 foo == null,则永远不会执行 foo.bar()。
              3. 它可以防止不良做法,例如捕获 NullPointerExceptions(大部分时间是由于代码中的错误)
              4. 它的执行速度应该与其他方法一样快,甚至更快(尽管我认为几乎不可能注意到它)。

              【讨论】:

                【解决方案14】:

                方法 4 无疑是最好的,因为它清楚地表明会发生什么并且使用最少的代码。

                方法 3 在各个层面都是错误的。您知道该项目可能为空,因此这不是特殊情况,您应该检查一下。

                方法 2 只是让它变得比它需要的更复杂。

                方法一就是方法四,多了一行代码。

                【讨论】:

                • 方法 3 是您通常在 Python 中执行的操作。而不是“您知道该项目可能为空,因此这不是特殊情况”,您可以说“您知道在特殊情况下(例如某个文件丢失某处)它将为空”。我不知道为什么 java 开发者如此害怕异常。如果您仍然必须在任何地方检查返回值,那么向语言添加异常功能有什么意义?使用try .. catch 的周围代码是否会降低性能?有充分的理由,还是只是 Java 风格?
                • @rjmunro 至少与常规返回相比,Java 中的异常是昂贵的。异常必须捕获​​一个非常昂贵的堆栈跟踪,尽管我隐约记得几年前针对确实使用异常进行控制流的人进行了优化。在 Java 中,如果您希望从文件中读取并且它不存在,那么将异常用于异常是很常见的。至于 null 检查,我有点同意,Java 现在有多种处理 null 的方法,例如新的 Optional 类。
                • 在性能方面,我对异常发生的性能不感兴趣 - 最坏的情况是用户收到错误消息之前的一两秒 - 我对围绕 with@ 的性能感兴趣987654322@ 与 if (foo != null) { ... }。假设该值不为空,显式空检查是否比 try catch 更快,并且如果您例如有几个可以为空的东西,你用 try catch 包围整个东西,或者需要 try catch 来处理其他错误并添加额外的 catch?
                • 空值检查将至少快一千倍(可能数千倍)。对于进入 catch 块的执行路径,必须引发异常,这意味着生成堆栈跟踪等。我不确定,但空检查几乎肯定会下降到本机指令,因此它尽可能接近尽你所能。只要没有引发异常,try catch 块对性能的影响就可以忽略不计。
                【解决方案15】:

                方法 4 是我的首选方法。 && 运算符的短路使代码最易读。方法 3,Catching NullPointerException,在大多数情况下不赞成使用简单的 null 检查就足够了。

                【讨论】:

                  【解决方案16】:

                  我会说方法 4 是我看过的代码中最通用的习惯用法。但这对我来说总感觉有点臭。它假设 foo == null 与 foo.bar() == false 相同。

                  这对我来说并不总是正确的。

                  【讨论】:

                    【解决方案17】:
                    • 不要抓到NullPointerException。这是一个不好的做法。最好确保该值不为空。
                    • 方法#4 适合您。它不会评估第二个条件,因为 Java 有短路(即,如果后续条件不改变布尔表达式的最终结果,则不会评估后续条件)。在这种情况下,如果逻辑 AND 的第一个表达式的计算结果为 false,则不需要计算后续表达式。

                    【讨论】:

                    • 值得一提的是为什么捕获空指针异常是不好的做法;异常确实非常昂贵,即使是少数也会真正减慢您的代码
                    • @RichardTingle 在大多数情况下,异常的成本可以忽略不计(java 在所有地方都使用它们)更多的是 NullpointerException 不仅可能来自 foo ,而且可能来自 try{ 和 }catch 之间的任何地方你可能会通过捕捉它来隐藏一个错误
                    • @josefx 一次做(几乎)任何事情的成本可以忽略不计,显然只有当它所在的代码部分是瓶颈时才相关;但是,如果它是那么它可能是原因。我收集了一些数据来回答这个问题stackoverflow.com/questions/16320014/…。显然,由于您提到的原因(除此之外还有许多其他原因),这也是一个坏主意。可能我应该说“原因之一”而不是“原因”
                    • @RichardTingle 异常不像以前那么昂贵(尤其是在您不需要检查堆栈跟踪的情况下)。除此之外,我同意 - 但是,对我来说,原因是异常会按照读者的预期破坏程序流程。
                    【解决方案18】:

                    最后一个也是最好的。即逻辑与

                      if (foo != null && foo.bar()) {
                        etc...
                    }
                    

                    因为在逻辑&amp;&amp;

                    不需要知道右手边是什么,结果一定是假的

                    喜欢阅读:Java logical operator short-circuiting

                    【讨论】:

                    • 用这种方法我发现自己得到了operator &amp;&amp; cannot be applied to booleanif (object.getObjectMethod() != null &amp;&amp; object.getObjectMethod().getNextThing())
                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2020-12-08
                    • 2020-04-05
                    • 1970-01-01
                    • 1970-01-01
                    • 2014-12-20
                    • 2017-11-15
                    • 2013-02-21
                    相关资源
                    最近更新 更多