【问题标题】:Avoiding Null Pointer Exception in "for" statement在“for”语句中避免空指针异常
【发布时间】:2014-05-20 21:05:08
【问题描述】:

在我的代码中,当 List 为空时,我通常使用这种方法来避免在 for 语句中出现 NullPointerExceptions:

if (myList != null && myList.size() > 0) {
    for ( MyObj obj : myList ) {
        System.out.println("MyObjStr: "+obj);
    }
}

是否有另一种方法可以不写“if”语句,但使用相同的“for”语句?

【问题讨论】:

  • 如果你已经初始化了myList,没有理由去检查它是否是null,除非在某些时候你明确地给它分配了null。关于尺寸,你不必检查它。
  • 您可以通过调用它的构造函数来轻松避免集合为空。在这种情况下,您将拥有非空但为空的集合。如果是这样,你甚至不需要尺寸检查
  • 我想知道如果您正在检查 myList 不是 null 那么您需要在哪里检查 myList大小大于 0?我的意思是,如果您的列表不为空,那么它必须有大小并且列表不能有负大小,所以我认为 myList.size() > 0 在您的情况下是不必要的, 除非它被初始化。
  • @AmirAl 大小检查是为了防止大小为 0,而不是负数。即使列表已初始化,它的大小也可能为 0(实际上,如果您不添加任何项目,它确实如此)。 无论如何,不​​需要此检查,因为for 循环不会对空列表执行任何操作。

标签: java collections arraylist


【解决方案1】:

首先,不需要尺寸检查。如果大小为 0,则跳过 for 循环。

null 检查更符合上下文。一般来说,您不想只是“避免 NPE”。 NPE 是一种发现编程错误的好方法,因此您必须注意需要对哪些内容进行空值检查。许多程序员出于防御性而到处使用空检查,因为他们不再信任 API 合约。

  • 如果你有一个 NPE 的东西没有理由为 null,那么你之前做错了什么(这里可能是因为你的集合变量没有初始化)。你不想隐藏它,你想让它在你的脸上爆炸,所以你可以立即修复它(快速失败原则)。

  • 如果 null 是变量的可能(且有意义的)值(或者如果该值来自外部源),则需要对其进行空值检查。但是,您必须处理这种情况,不要只是跳过产生 NPE 的代码。如果你只是跳过它,那么后面的代码并不比当前代码更安全,并且需要再次对同一个变量进行空检查。

@ifLoop 提供了一个非常好的链接,指向有关此主题的另一篇文章,因此我将其复制到此处,因为我的回答已被接受: Avoiding “!= null” statements in Java?

【讨论】:

  • 很多关于这个的争论......我认为它不应该应用于任何地方。例如,如果您依赖的外部服务开始返回空集合,您不希望您的应用停止运行...
  • 当然,我完全同意你的看法。我不是说你不想要空检查。我说你不想只是“避免 NPE”。如果null 是外部输入的可能值(以及其他几种情况),则必须正确完成检查和处理。但是,如果某事没有理由在某一时刻为空,则不要进行空检查,它会隐藏错误。
  • 在我们的具体情况下,空值检查不是用来处理特殊情况,而只是为了跳过错误并隐藏它,恕我直言,这是不好的。
  • 我认为发明者承认 null 是十亿美元的错误 - medium.com/.../…
【解决方案2】:

在很多情况下,您应该将传入的 DTO 映射到另一个对象。发送空集合而不是 null 可能会浪费流量,甚至可以通过使用排除 null mapper.setSerializationInclusion(Include.NON_NULL); 甚至是mapper.setSerializationInclusion(Include.NON_EMPTY);

也就是说,如果您需要执行以下操作:

Collection<Dto> dtos = null;
for(Dto dto: dtos) {
   //doSomething
} 

你必须用if保护(好吧,但我更喜欢另一种方式)或坚持NullObject成语(好):

Collection<Dto> safeDtos = Optional.ofNullable(dtos).orElse(Collections.emptyList());
for(Dto dto: safeDtos) {
   //doSomething
} 

或者相同,但对于流:

Optional.ofNullable(dtos).orElse(Collections.emptyList()).stream()
    .map(this::doSomething)
    .collect(Collectors.toList());

此外,除了映射练习之外,尽量不要在任何下游代码中泄漏 DTO。您应该在 DTO 和其余代码之间设置保护边界。

DTO -> 映射 -> 域对象(或另一个 NPE 保存的中间对象,可能有枚举而不是字符串等) -> 其余代码。

【讨论】:

    【解决方案3】:

    还有另一种可能避免将 NULL 传递到我们的 for (forEach) 循环中 - 我们可以使用 CollectionUtils 库中的 isNotEmpty(Collection coll) 方法。

    示例:

    if (CollectionUtils.isNotEmpty(someCollection)) {
        for (Item item: someCollection) {
        ...
        }
    }
    

    或:

    if (CollectionUtils.isNotEmpty(someCollection)) {
        someCollection.forEach(...);
    }
    

    【讨论】:

      【解决方案4】:

      可以在similar post 中找到一种优雅的方法。使用

      for( Object o : safe( list ) ) {
         // do whatever 
       }
      
      public static List safe( List other ) {
          return other == null ? Collections.EMPTY_LIST : other;
      }
      

      也就是说,让您的方法返回空数组而不是 null 是一个好习惯。例如你可以返回

      return Collections.EMPTY_LIST;
      

      在每个catch 块中。

      这样您将安全地循环它们,当您获得 NPE 时,您会知道您的代码有问题,而不是您的数据。在这种情况下,正如@Joffrey 所说,NPE 将非常受欢迎

      【讨论】:

      • 在给定的解决方案中这是最优雅的
      【解决方案5】:

      不需要检查大小。如果列表中没有对象,您的 for 循环将不会执行。

      仅当您不确定对象的状态时,才需要检查 null。但是,当使用您自己的对象时(例如,没有通过争论从外部给出),则不需要进行 null 检查。

      请参阅 Avoiding “!= null” statements in Java?,了解为什么没有经验的开发人员经常使用空检查来夸大其词。

      【讨论】:

        【解决方案6】:

        好吧,你可以做到这一点,但会降低可读性:

        for ( MyObj obj : (myList == null ? new ArrayList<MyObj>() : myList) ) {
            System.out.println("MyObjStr: "+obj);
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-10-01
          • 2019-04-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-02-18
          相关资源
          最近更新 更多