【问题标题】:How to show if a method may return null如何显示方法是否可能返回 null
【发布时间】:2010-09-29 12:19:48
【问题描述】:

在发布this question 并阅读that one 之后,我意识到知道一个方法是否应该返回null 或者这是否被认为是错误条件并且应该抛出异常是非常重要的。 return ‘null’ or throw exception 时也有很好的讨论。

我正在编写一个方法,并且我已经知道是否要返回 null 或抛出异常,表达我的决定的最佳方式是什么,换句话说,记录我的合同?

我能想到的一些方法:

  • 在规范/文档中写下来(有人会读吗?)
  • 让它成为方法名称的一部分(正如我建议的here
  • 假设每个抛出异常的方法都返回null,而每个“不”抛出可能的方法都返回null。

我主要是在谈论 java,但它也可能适用于其他语言:为什么有一种正式的方式来表达是否会抛出异常(throws 关键字)但没有正式的方式来表达如果 null 可能被退回?

为什么没有类似的东西:

public notnull Object methodWhichCannotReturnNull(int i) throws Exception
{
    return null; // this would lead to a compiler error!
}

总结与结论

合同的表达方式有很多种:

  • 如果您的 IDE 支持它(如 IntelliJ),最好使用像 @NotNull 这样的注解,因为它对程序员可见并且可用于自动编译时检查。有一个 plugin for Eclipse 可以添加对这些的支持,但它对我不起作用。
  • 如果这些不是一个选项,请使用 Option<T>NotNull<T> 等自定义类型,这样可以增加清晰度并至少进行运行时检查。
  • 无论如何,在 JavaDoc 中记录合同永远不会有害,有时甚至会有所帮助。
  • 使用方法名称来记录返回值的可空性不是由我以外的任何人提出的,虽然它可能非常冗长且并不总是有用,但我仍然相信有时它有其优势,也是。

【问题讨论】:

标签: java null design-by-contract


【解决方案1】:

一个很好的后续问题。我认为null 是一个真正特殊的值,如果一个方法可能返回null,它必须在Javadoc 中明确记录它(@return some value ..., or null if ...)。在编码时我是防御性的,并假设一个方法可能会返回 null,除非我确信它不能(例如,因为 Javadoc 是这样说的。)

人们意识到这是一个问题,建议的解决方案是使用注释以一种可以自动检查的方式陈述意图。请参阅JSR 305: Annotations for Software Defect DetectionJSR 308: Annotations on Java TypesJetBrain's Nullable How-To

您的示例可能看起来像这样,但被 IDE、编译器或其他代码分析工具拒绝。

@NotNull
public Object methodWhichCannotReturnNull(int i) throws Exception
{
    return null; // this would lead to a compiler error!
}

【讨论】:

  • 很好的答案:在 javadoc 中明确地编写它是我喜欢看到的(如 stackoverflow.com/questions/61604 中所述)。但是,我很少从同事那里看到任何 complete javadoc。有时,我根本看不到任何 javadoc,因为“代码本身就是一个文档”...啊
  • 不管怎样,如果你的构建链中有 FindBugs,它现在支持检查 @NonNull 和类似提示。
  • JSR 似乎已经有一段时间了,实际上是从 2006 年开始......通常需要这么长时间来进行这样的 更改吗?
  • 很可能是人们意识到JSR的范围太窄,让工具来处理。似乎这现在是 JSR 308 的一个特性:Java 类型的注释。
  • 如果有人不清楚这一点,JSR 308:“具体的注解名称,例如 @NonNull,只是示例;本文档不建议任何注解,只是指定它们可以出现在 Java 代码中的什么位置。”
【解决方案2】:

您可以使用Option 类型,它非常类似于具有零个或一个元素的列表。 Option<Object> 的返回类型表示该方法可能返回 Object,或者它可能返回 None 类型的特殊值。这种类型是使用更好的类型检查的 null 的替代品。

例子:

public Option<Integer> parseInt(String s) {
   try {
      return Option.some(Integer.parseInt(s));
   }
   catch (Exception e) {
      return Option.none();
   }
}

如果你一直使用它,你可以打开 IDE 空警告,或者只使用 grep 来处理 null,如果你在任何你通常使用 null 的地方使用 Option.none(),它根本不应该出现在你的代码中字面意思。

Option 是 Scala 的标准配置,在 Haskell 中称为Maybe。上面的链接指向一个名为Functional Java 的库,其中包含它。该版本实现了Iterable 接口,并具有可让您很好地组合事物的单子方法。例如,在 None 的情况下提供默认值 0:

int x = optionalInt.orSome(0);

你可以替换这个...

if (myString != null && !"".equals(myString))

...有了这个,如果你有一个Option&lt;String&gt;...

for (String s : myOptionString)

【讨论】:

  • 听起来很有趣。但我需要考虑一段时间才能知道它是否真的有帮助。
  • 只是一个补充,Java 1.8 现在有 Optional 类,它与 Guava 中的 Option 做同样的事情。
  • 6 年后,Java 1.8 现在有了一个用处不大的 Optional 类。
  • Optional 类是我听说过的对 Java 最愚蠢的补充之一。如此有用的编程功能实现得如此糟糕,以至于它唯一的成就就是将NullPointerException 替换为NoSuchElementException
  • 我喜欢这种方法的想法,但仅限于新项目。重构使用 null 来使用它的现有代码将是一场噩梦。
【解决方案3】:

有人支持@Nullable and @NotNull annotation in IntelliJ IDEA。还有一些关于将这些注释(或类似功能)添加到 Java 7 的讨论。不幸的是,我不知道这已经走了多远,或者它是否仍然在轨道上。

【讨论】:

    【解决方案4】:

    确实:在我们的框架中,我们有一个“非空”指针类型,它可能被返回以表明该方法将始终返回一个值。

    我看到三个选项:

    1. 等待语言支持来表达它(例如 C# ?! 事物)
    2. 使用 Aspect Orientation 构建您自己的语言扩展来表达它
    3. 使用自定义类型来表达它
    4. (但建立在开发者合作的基础上)使用命名方案来表示它

    【讨论】:

      【解决方案5】:

      对于 Java,可以使用方法的 Javadoc 描述来记录返回值的含义,包括它是否可以为 null。如前所述,注释也可以在这里提供帮助。

      另一方面,我承认我不认为 null 是什么可怕的事情。在某些情况下,“没人在家”是一个有意义的条件(尽管 Null Object 技术在这里也有真正的价值)。

      尝试对空值调用方法肯定会导致异常。但是尝试除以零也是如此。这并不意味着我们需要开展一场消除零的运动!这只是意味着我们需要了解方法的约定,并使用它返回的值做正确的事情。

      【讨论】:

      • 但是,如果合同没有明确提到 null,你就不能“理解合同”。这不是什么可怕的事情,但是需要检查它是否是有效的返回类型,合同应该明确说明,如 rblasch 的回答。代码更简洁,无需对 null 进行大量检查。
      【解决方案6】:

      你看过Spec#吗?

      【讨论】:

      • 不知道。看起来很有趣,但我总是对使用编程语言的衍生品持怀疑态度。我宁愿在官方语言核心中加入这样的功能。不过我会看看的。
      • 嗨,布莱恩。这是 Microsoft 的一个研究项目,可能/可能会包含在 C# 的未来版本中。
      • @LenaSchimmel 如果您希望将不可为空性作为非派生语言的一个特性,您应该看看 Kotlin。
      【解决方案7】:

      您可以编写自己的注释 (Java) 或属性 (C#) 来指示返回值可能为空。没有任何东西会自动检查它(尽管 .NET 4.0 会有 code contracts 来处理这类事情),但它至少可以作为文档。

      【讨论】:

        【解决方案8】:

        也许你可以定义一个名为“NotNull”的泛型类,这样你的方法可能是这样的:

        public NotNull<Object> methodWhichCannotReturnNull(int i) throws Exception
        {
           // the following would lead to a run-time error thown by the
           // NotNull constructor, if it's constructed with a null value
           return new NotNull<Object>(null);
        }
        

        这仍然是运行时(不是编译时)检查,但是:

        • 在方法的实现中抛出(不是调用代码的错误)
        • 它是自记录的(调用者知道他将 NotNull&lt;T&gt; 作为返回类型)

        【讨论】:

        • 这会将空检查放在一个地方,但它仍然是运行时检查。
        【解决方案9】:

        不惜一切代价避免依赖 JavaDocs。人们只有在签名看起来不是微不足道且不言自明的情况下才会阅读它们(这是不好的开始),而那些真正费心阅读它们的人不太可能在 null 上犯错,因为他们目前更加小心.

        【讨论】:

        • 加上 JavaDoc 可能不准确,例如过期。
        【解决方案10】:

        如果您使用的是 Java 5+,则可以使用自定义注解,例如@MayReturnNull

        更新

        抛开所有编码理念(返回 null、使用异常、断言、yada yada),我希望以上内容能回答您的问题。除了具有默认值的基元之外,复杂类型可能为空,也可能不为空,您的代码需要处理它。

        【讨论】:

          【解决方案11】:

          一般来说,我会假设默认情况下返回 null 值违反了 API 的约定。几乎总是可以设计您的代码,以便在“正常”执行流程期间永远不会从您的 API 返回空值。 (例如,检查 foo.contains(obj) 而不是调用 foo.get(obj) 并为 null 设置一个单独的分支。或者,使用 Null object pattern

          如果你不能以这种方式设计你的 API,我会清楚地记录何时以及为什么会抛出空值——至少在 Javadoc 中,并且可能还使用自定义 @annotation 等正如其他几个答案所建议的那样。

          【讨论】:

            猜你喜欢
            • 2020-09-17
            • 2020-04-15
            • 1970-01-01
            • 1970-01-01
            • 2011-07-11
            • 2013-06-11
            • 1970-01-01
            • 2015-09-01
            • 1970-01-01
            相关资源
            最近更新 更多