【问题标题】:Handling null in java streams with optional使用可选处理 Java 流中的 null
【发布时间】:2018-12-20 07:09:04
【问题描述】:

如果我们有下面这样的场景,处理 null 的最佳方法是什么

//mocking for demonstraton  
studentsByCourseRoster.setUsers(null);

studentsByCourseRoster.getUsers().stream().forEach(user -> {
    final UserDTOv2 userDTO = new UserDTOv2();
    userDTO.populateUserDataFromUserDTO(user, groupedUsers);
    users.add(userDTO);
});

【问题讨论】:

  • 你要在哪里处理null
  • if(studentsByCourseRoster.getUsers() != null) 怎么样?
  • 只是不要将用户设置为null。允许List 变为null(而不是空的)是软件设计缺陷的标志。与其在阅读端添加变通方法,不如修复设计。

标签: java java-8 null optional


【解决方案1】:

如果要保留单语句结构,可以使用Optional.ofNullable 并将null 替换为空列表:

Optional.ofNullable(studentsByCourseRoster.getUsers())
        .orElse(Collections.emptyList())
        .forEach(user -> {
                     final UserDTOv2 userDTO = new UserDTOv2();
                     userDTO.populateUserDataFromUserDTO(user, groupedUsers);
                     users.add(userDTO);
         });

【讨论】:

  • 你不需要.stream()顺便说一句,事实上,这违反了.stream()的规范
  • @Eugene 好点谢谢。我试图从 OP 的代码中进行最小的更改,但你是对的,stream() 调用是多余的。我删除了它。
【解决方案2】:

对 Mureinik 的回答稍作修改,我会改用:

List<UserDTOv2> users = Optional.ofNullable(studentsByCourseRoster.getUsers())
        .orElse(Collections.emptyList())
        .stream()
        .map(user -> {
            UserDTOv2 userDTO = new UserDTOv2();
            userDTO.populateUserDataFromUserDTO(user, groupedUsers);
            return userDTO;
        }).collect(Collectors.toList());

在 Java 9 中使用 Stream.ofNullable

List<UserDTOv2> users = Stream.ofNullable(studentsByCourseRoster.getUsers())
        .map(user -> {
            UserDTOv2 userDTO = new UserDTOv2();
            userDTO.populateUserDataFromUserDTO(user, groupedUsers);
            return userDTO;
        }).collect(Collectors.toList());

【讨论】:

    【解决方案3】:

    如果我们有类似的情况,处理 null 的最佳方法是什么 下面

    在您当前的设计下,最简单的解决方案是使用if 语句。

    这并不是说它是“处理”这个问题的最佳方式,而是处理这个问题的最佳方式这绝不是一开始就允许列表处于空状态cmets中提到的。

    有一个空列表作为默认值,这将为您节省大量的 if 检查代码库,具体取决于 getUsers() 被使用的次数,最重要的是,您不必担心NullPointerExeception's 因为它们不应该发生。

    另一方面,每当您似乎看到自己在某个收藏中调用stream(),然后立即调用forEach,您应该意识到这是错误的; 1) 从某种意义上说,您可以轻松地直接在列表上调用forEach,即studentsByCourseRoster.getUsers().forEach(...) 2) 流和副作用不能很好地协同工作。

    【讨论】:

      【解决方案4】:

      还可以通过filter(Objects::nonNull)过滤空对象:

      studentsByCourseRoster.getUsers().stream().filter(Objects::nonNull).forEach(user -> {
          final UserDTOv2 userDTO = new UserDTOv2();
          userDTO.populateUserDataFromUserDTO(user, groupedUsers);
          users.add(userDTO);
      });
      

      【讨论】:

      • 实际上,第一手的列表是空的,所以你会在studentsByCourseRoster.getUsers()得到NPE
      • 如果有空集合,我不会得到 NPE。 Streams 处理一个源的所有元素,如果没有元素,则不执行任何操作。
      • 也许您应该尝试使用studentsByCourseRoster.setUsers(null); 运行您的代码,即使IDE 也可以检测到Null 变量访问
      【解决方案5】:

      除了更正@Murenik 的答案之外,还有一种可选的编写方式。不需要创建空列表,我们可以只传递执行非空情况:

          Optional.ofNullable(nullableUsers)
                  .ifPresent(users -> users.forEach(user -> {
                      // work with user
                  }));
      

      Nullable optional 也适用于嵌套的空引用,例如我们有一些结构:

      class User {
          @Getter
          Address address;
      }
      
      class Address {
          @Getter
          String street;
      }
      

      那就不用写了

      if (user.getAddress() != null && user.getAddress().getStreet() != null) {
         // work with street
      }
      

      我们可以使用可选的:

          Optional.ofNullable(user)
                  .map(User::getAddress)
                  .map(Address::getStreet)
                  .ifPresent(street -> {
                      // work with street
                  });
      

      【讨论】:

        猜你喜欢
        • 2019-04-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-05-19
        • 2020-07-14
        • 1970-01-01
        • 2018-04-29
        相关资源
        最近更新 更多