50

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

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

我能想到的一些方法:

  • 把它写在规格/文档中(有人会读吗?)
  • 使其成为方法名称的一部分(正如我在这里建议的那样)
  • 假设每个抛出异常的方法都不会返回 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因为它对程序员可见并且可用于自动编译时检查。Eclipse有一个插件可以添加对这些的支持,但它对我不起作用。
  • 如果这些不是一个选项,请使用类似Option<T>or的自定义类型NotNull<T>,这可以增加清晰度并至少进行运行时检查。
  • 无论如何,在 JavaDoc 中记录合同永远不会有害,有时甚至会有所帮助。
  • 使用方法名称来记录返回值的可空性不是由我以外的任何人提出的,尽管它可能非常冗长且并不总是有用,但我仍然相信有时它也有其优点。
4

11 回答 11

39

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

人们意识到这是一个问题,建议的解决方案是使用注释以一种可以自动检查的方式来说明意图。请参阅JSR 305:软件缺陷检测注释JSR 308:Java 类型注释JetBrain 的 Nullable How-To

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

@NotNull
public Object methodWhichCannotReturnNull(int i) throws Exception
{
    return null; // this would lead to a compiler error!
}
于 2009-01-04T18:53:07.020 回答
9

您可以使用该Option类型,它非常类似于具有零个或一个元素的列表。返回类型 ofOption<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

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

int x = optionalInt.orSome(0);

你可以替换这个...

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

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

for (String s : myOptionString)
于 2009-01-04T18:45:10.107 回答
2

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

于 2009-01-04T18:51:50.563 回答
2

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

我看到三个选项:

  1. 等待语言支持来表达它(例如C# ?!的东西)
  2. 使用 Aspect Orientation 构建您自己的语言扩展来表达它
  3. 使用自定义类型来表达它
  4. (但建立在开发者合作的基础上)使用命名方案来表示它
于 2009-01-04T18:53:38.343 回答
2

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

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

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

于 2009-01-04T20:43:23.020 回答
1

你看过Spec#吗?

于 2009-01-04T18:36:36.413 回答
1

您可以编写自己的注释 (Java) 或属性 (C#) 来指示返回值可能为空。没有任何东西会自动检查它(尽管 .NET 4.0 将有此类事情的代码协定),但它至少会充当文档。

于 2009-01-04T18:43:16.133 回答
1

也许您可以定义一个名为“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>作为返回类型得到的)
于 2009-01-04T18:58:48.660 回答
1

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

于 2009-01-04T20:48:17.147 回答
0

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

更新

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

于 2009-01-04T18:45:37.407 回答
0

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

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

于 2009-01-04T19:03:15.627 回答