发帖后 这个问题 和阅读 那个 我意识到知道一个方法是否应该返回 null,或者这是否被视为错误条件并且应该抛出异常非常重要。还有一个很好的讨论何时 返回“null”或抛出异常 .

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

我能想到的一些方法:

  • 把它写在规格/文档中(有人会读吗?)
  • 使其成为方法名称的一部分(正如我建议的那样 这里)
  • 假设每个方法 投掷 例外情况将 不是 返回 null,并且每一个“不”抛出的 可能 返回空值。

我主要讨论的是 java,但它也可能适用于其他语言:为什么有一种正式的方式来表达是否会抛出异常( throws keywords),但没有正式的方式来表达是否可能返回 null?

为什么没有这样的东西:

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

总结与结论

契约的表达方式有很多种:

  • 如果您的 IDE 支持它(如 IntelliJ),最好使用类似的注释 @NotNull 因为它对程序员是可见的并且可以用于自动编译时间检查。有一个 Eclipse 插件 添加对这些的支持,但这对我不起作用。
  • 如果这些不是一个选项,请使用自定义类型,例如 Option<T> 或者 NotNull<T>, ,这增加了清晰度并至少增加了运行时检查。
  • 无论如何,在 JavaDoc 中记录契约从来没有坏处,有时甚至有帮助。
  • 使用方法名称来记录 可空性 除了我之外,没有人提出返回值的概念,尽管它可能非常冗长并且并不总是有用,但我仍然相信有时它也有其优点。
有帮助吗?

解决方案

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

人们意识到这是一个问题,提出的解决方案是使用注释以可以自动检查的方式来陈述意图。看 JSR 305:软件缺陷检测注释, JSR 308:Java 类型的注释JetBrain 的可为空操作方法.

您的示例可能如下所示,并被 IDE、编译器或其他代码分析工具拒绝。

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

其他提示

您可以使用 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 如果您使用,则根本不应该出现在您的代码中 Option.none() 你通常会使用的任何地方 null 文字。

Option Scala 的标准配置,它被称为 Maybe 在哈斯克尔。上面的链接是一个名为 函数式Java 其中包括它。该版本实现了 Iterable 接口,并且具有一元方法,可以让您很好地组合事物。例如,在以下情况下提供默认值 0 None:

int x = optionalInt.orSome(0);

而且你可以替换这个...

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

...有了这个,如果你有 Option<String>...

for (String s : myOptionString)

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

我看到三个选项:

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

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

另一方面,我承认我并不认为 null 是值得害怕的东西。在某些情况下,“无人在家”是一个有意义的条件(尽管空对象技术在这里也具有实际价值)。

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

你看过吗 规格#?

您可以编写自己的注释 (Java) 或属性 (C#) 来指示返回值可能为 null。没有什么会自动检查它(尽管 .NET 4.0 将具有 代码合约 对于这类事情)但它至少可以充当文档。

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

也许您可以定义一个名为“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<T> 作为返回类型)

不惜一切代价,避免依赖 JavaDocs。人们只有在签名看起来不琐碎且不言自明的情况下才会阅读它们(这从一开始就很糟糕),而那些真正费心阅读它们的人不太可能在空值上犯错误,因为他们目前更加小心。

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

更新

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

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top