【问题标题】:Scala avoid using nullScala 避免使用 null
【发布时间】:2017-09-12 20:38:03
【问题描述】:

我在 github 上有一个由 codacy 分析的项目。分析建议对以下代码行“避免使用null”

def doSomethingWithPath(path:Path) = {
    require(path != null, "Path should not be null") //<-to avoid
    ...
}

修复它的最简单的 scala 惯用方法是什么?

【问题讨论】:

  • 考虑接受其中一个答案,或评论您认为它们不是解决方案的原因。这样,最终提出您问题的其他用户将获得有关如何解决相同问题的更多信息

标签: scala idioms preconditions codacy


【解决方案1】:

我会保持原样。你在这里并没有真正使用 null ,只是为了防止它。另一种方法可能是完全删除该行,并决定根本不处理 null。在构建良好的代码库中忽略 null 的可能性可能很好,无论如何它都不应该出现,并且 null 将是一个错误,但是一个简单的保护措施可以防止出现更微妙的错误,以防出现问题和null 实际发生了。

【讨论】:

    【解决方案2】:

    如果path 可能真的 null 这可能是最简单的。

    require(Option(path).isDefined, "Must have a real Path")
    

    【讨论】:

    • path != nullOption(path).isDefined 更有意义。它完成了同样的事情,但避免了完全不必要的对象分配。我也觉得也清楚一点。
    • @puhlen,我同意。我还认为@pedrorijo91 提出了一些有效/有价值的观点。但是OP要求在这种情况下避免null的“最简单”方法,这就是我试图提供的。
    【解决方案3】:

    最惯用的方法是避免 require(不确定,但我认为它会引发异常 - 这是 Scala 强烈建议反对的)

    def doSomethingWithPath(path:Path): Option[stuff] = { // will return an option of the type you were returning previously.
         Option(path).map { notNullPath =>
           ...
         }
    }
    

    现在可能的null 情况将返回给调用者,调用者可以/将传播返回的选项,直到知道如何正确处理它的层。

    注意:处理 null 情况的最佳位置可能是在您的函数内部。在这种情况下,您应该执行类似的操作

    Option(path).map { notNullPath =>
        ...
    }.getOrElse(/* take care of null case */)
    

    如果您确实想保留require,那么answer by jwvh 也是我的选择

    【讨论】:

    • 这只会让事情变得不必要地复杂化。而不是“用路径做某事”,现在你的功能是“如果你给我一个,也许可以用路径做某事,但如果你给我 None,默默地不要”。如果用户想要使用选项,他们可以map 原始功能而不是他们自己的选项。如果他们不想使用选项,他们不应该做doSomethingWithPath(Option(path)).get。它甚至不是真正更安全,因为您的 Option 可能为 null 甚至 Some(null)。
    【解决方案4】:

    无需显式检查 null 或将 path 包装在 Option 中。 你可以这样做:

     path match {
      case p: String => Option(doSomethingWith(p))
      case _         => None //if path is null this will be returned
     }
    

    这将返回一个Option,这可能不是您想要的,但在这种情况下,不是产生一个None,而是引发一个异常。 require 无论如何都会在您的示例中引发异常,所以如果这是您的调用者所期望的,请明确执行。

    【讨论】:

      【解决方案5】:

      问题

      禁止使用 null 是最佳实践。 This article explains whyGuava:即席错误处理、模糊语义、缓慢失败等。

      编写 require 来自于在不满足调用方法的先决条件时快速失败和清理的需要。问题在于,正如@puhlen 解释的那样,它只用另一个异常(尽管更具描述性)替换了 NPE。

      理想的解决方案

      在理想世界中,path:Path 将与number:Int 相同,并且不需要测试对象是否存在。问题是 scala(与许多其他语言一样)允许 null 破坏纯面向对象的方法。

      中间解决方案

      java/scala 编译器应该强制 Optional 类型作为管理 null 的唯一代码并强制 null 在类型系统中存在。在这种情况下,任何使用 null 都可能被视为编译错误。我不知道这是否完全可行。

      使用@NotNull/@Nullable 注解

      由于没有语言/编译器级别的默认行为,因此库之间的阻抗不匹配。

      实用解决方案

      用 Predef2 用最少的样板逻辑定义我自己的类。我仍然只会得到一个“避免使用null”或使用guava Preconditions.checkNotNull

      object Predef2{
          def requireNotNull(object:AnyRef) = 
             require(path != null, "Some object should not be null")
      }
      def doSomethingWithPath(path:Path) = {
          requireNotNull(path)
          ...
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-02-05
        • 2017-03-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多