返回NULL的替代方法

/** * Returns the foo with the matching id in this list * * @param id the id of the foo to return * @return the foo with the matching id in this list */ public Foo getFoo(int id) { for (Foo foo : list) { if (foo.getID() == id) { return foo; } } return null; } 

如果找不到foo而不是返回null ,我应该throw exception吗? 这是否重要,是否有关于这个问题的“最佳实践”成语? 顺便说一句,我知道我的例子有点做作,但我希望你明白这个想法……

谢谢。

编辑

更改代码以基于id获取Foo以更好地说明真实场景。

返回null不仅更易于处理,性能也更好。 必须使用例外来处理特殊情况。

我会说这取决于你的方法的语义。

foo几乎总能在列表中找到? (例如,如果它是一个包含有限数量对象的缓存)。 如果是这样,那么找不到可能意味着出现了问题 – 例如,某些应用程序初始化失败,或者密钥无效 – 并且exception可能是合理的。

但是,在大多数情况下,我会返回null 。 也许客户端知道对象可能不在那里并且具有编码逻辑来处理该情况; 如果您使用了exception,则该代码将更难以阅读,理解和维护。

一些API实际上提供了两种方法:可能返回null的find方法,以及抛出exception的getload方法。

嗯…当有疑问时,错误地支持null 🙂

如果将其记录为有效结果,则返回null是正常的。

另一种选择是空对象模式。 那是 – 没有任何数据的Foo实例:

 public class Foo { public static final Foo NULL_FOO = new Foo(); } 

并返回它。

我更喜欢返回null 。 从方法返回这是一个非常好的结果,调用该方法的代码应该适当地处理空值。 这可能意味着在您的调用代码中抛出exception,但我不会在此方法中执行此操作。

这样,如果其他人想要在线下调用您的方法,他们可以以不同于您选择的方式处理空值。 如果你抛出exception,它可能会迫使另一个程序员以不同于他们预期的方式改变他们的代码。

当然,在某些情况下,如果某些东西是null(例如,连接对象或类似的东西,你实际上需要有一个值,如果你不这样做那么意味着什么是错误)。 但是,根据经验,在大多数情况下,你应该很好地返回null。

如果可以的话,最好避免例外,但有时你不能。 在这种情况下,如果您在列表中存储了null ,该怎么办? 你无法区分’找到null’和’找不到你想要的’之间的区别。

有一种模式,它被称为选项类型 ,它在Scala中使用了很多。 您要么返回带有该项的容器,要么返回一个类别为“我为空”的容器。 维基文章将提供更好的图片。

你也可以拥有’这个集合中是否存在这个?’ 返回bool的方法,如果你没先检查,则从上面的代码中抛出exception。


而且只是与您的实际问题完全无关的评论。 如果你实现了equals,它应该只返回true,如果这两个对象实际上被认为是相等的。 因此,上面的代码必须始终返回您传递给它的对象!

最好的做法是在Javadoc中说没有找到匹配时返回null

另一种方法可能是返回一个可能为空的匹配列​​表(并且可能有多个)但是我不喜欢这种方法。

另一种方法可能是返回类型为Foo的NULL_FOO值。

我宁愿只返回null。

解决此问题的一种方法是查看您要对值进行的操作并丰富函数,以便在方法中使用返回的值而不返回。 例如,如果要调用带有值的方法,只需在函数中调用它,避免返回任何内容。

majory它取决于场景。 如果您的应用程序本身就是此方法的生产者和使用者,则完全由您决定要做什么,否则您需要根据方法的使用情况和客户需求来决定。

返回null是完全可以接受的。 我会去额外几十次击键并记录在JavaDoc中返回null的可能性。

抛出已检查的exception意味着您必须在调用方法的任何地方尝试/捕获或重新抛出该exception。 一个未经检查的例外,特别是除了NPE之外的任何事情,都会让人感到意外。

在这种情况下,由于您要定义一个访问器,它应该返回null。 如果是另一种方法,此方法应该保证非空响应,则exception更合适。

但是,作为旁注,不是将示例方法称为getter,而是将其命名为Foo findFoo(Foo f)可能更合适,因为您正在搜索而不仅仅是获取。

我认为这是一个品味问题,在这种情况下,它还取决于列表是否可能包含空值。 如果列表可能包含空值,则null也将是有效的方法参数,您需要区分返回null(例如,传递,找到并返回null)或告诉方法调用者找到传递的空值。

这可能不会直接回答你的问题,但它来自我从Stackoverflow成员那些关于类似主题的一些评论。

如果找不到foo,而不是返回null,我应该抛出exception吗? 这是否重要,是否有关于这个问题的“最佳实践”成语? 顺便说一句,我知道我的例子有点做作,但我希望你能得到这个想法…“

从我收集的内容中,当exception涉及给予方法的参数时,应该从方法抛出exception。 例如,接受File实例的方法会抛出NullPointerException或IOException。 这是因为调用者和被调用者之间存在一个契约,即调用者应该发送有效对象并在它们无效时处理它们。

此外,您需要决定是否处理前置条件和后置条件。 您可以在方法的开头放置一个保护来处理参数,这可以节省相当多的代码。 但是,有些人认为这是一种不正确的方法,因为在UI中应该事先进行一些validation。

要完成,如果目的是检索单个实例,则返回null对象是完全有效的。 这是为了解释一个对象未​​被发现或不存在。 说到对象组,我认为约定只是返回一个空的Collection / List。

我会说这取决于你的应用程序以及如何使用该方法。

  1. 如果您希望“未找到”结果经常发生,它可能会对性能*产生负面影响,则返回null,并在调用者中检查它。
  2. 如果“未找到”结果将相对罕见或将导致您的应用程序放弃请求,那么抛出exception将没有问题。 exception可以使您的代码更容易阅读,因为您可以保证成功返回将生成一个对象,因此您不必对其进行空值检查。

我赞同这样的观点,即exception应该用于特殊条件,这是错误条件的超集。 我还采用了Spring / Hibernate对未经检查的exception的偏好,这避免了繁琐的try / catch逻辑。

*使用exception与返回null没有太多的性能开销。 我只是做了一个快速测试,调用方法需要4ms才能返回null,并且只需要535ms来调用另一个方法数百万次抛出exception。

我没有在其他答案中读到的另一种观点如下:

list是否包含Foo null ? 如果是这种情况,则不知道是否找到了id,或者是否与null相关联。 在这种情况下:

  • 抛出未找到列表的exception;
  • 将结果包装在另一个对象中(例如, SearchResult ,它告诉您是否找到了对象,它是关联id,它是结果,如果没有找到则没有结果);
  • 创建方法existsFoo(int id)

每个解决方案都有自己的问题,这取决于使用的情况:

  • 正如其他人所指出的, Exception特殊的 ;
  • 每次您想重试搜索时,都必须分配一个包装器SearchResult 。 包装材料可以制成可变的 ,但这会带来许多新的困难和问题;
  • existsFoo必须搜索列表,该列表将知道密钥是否存在的成本加倍。

一般来说我可以说:

  • 不是特殊的ID? 使用IllegalArgumentException ;
  • 结果是否传递给其他类,用作对象? 使用包装器,例如SearchResult ;
  • 只检查getFoo是否为null(是否存在)? 使用另一种方法, existsFoo

虽然我同意编码null是简单易用的样板程序员调节,但它对引入的复杂性几乎没有什么价值。 总是假设非空引用使得代码稍微少一些的逻辑更清晰 – 您仍然需要测试失败。

下面的注释摘录将帮助您不要陷入旧的方式……

使用@Nullable

要消除代码库中的NullPointerExceptions ,必须遵守有关空引用的规定。 通过遵循并执行一条简单的规则,我们在此方面取得了成功:

除非明确指定,否则每个参数都是非null。

Guava:适用于Java的Google核心库和JSR-305具有简单的API,可以控制空值。 如果找到空引用,则Preconditions.checkNotNull可用于快速失败,并且@Nullable可用于注释允许空值的参数:

 import static com.google.common.base.Preconditions.checkNotNull; import static javax.annotation.Nullable; public class Person { ... public Person(String firstName, String lastName, @Nullable Phone phone) { this.firstName = checkNotNull(firstName, "firstName"); this.lastName = checkNotNull(lastName, "lastName"); this.phone = phone; } 

如果您的类允许null,则可以使用@Nullable注释使用@Nullable注释来指定字段或参数,例如edu.umd.cs.findbugs.annotations.Nullable或javax.annotation.Nullable。

这是一个老问题但是我没有找到这里提到的guava的Optional类以及JDK的Optional (来自Java 8),它具有相同的用途并具有更多function。

本文很好地概述了使用guava的Optional的原因。 我强烈建议阅读它。

这是一段摘录:

重点是什么?

除了通过赋予null名称而增加可读性之外,Optional的最大优点是它的白痴certificate。 如果您希望程序完全编译,它会强制您主动考虑缺席情况,因为您必须主动解包可选并解决该情况。 Null使得简单地忘记事情变得非常容易,虽然FindBugs有所帮助,但我们认为它几乎不会解决这个问题。

当您返回可能存在或可能不存在的值时,这尤其重要。 你(和其他人)更有可能忘记other.method(a,b)可以返回一个空值而不是你可能会忘记当你实现other.method时a可能为null。 返回Optional使得调用者无法忘记这种情况,因为他们必须自己解包对象以便编译代码。

我个人认为,如果重要的是,在像Java这样已经冗长的语言中,返回null并不是那么可怕。 该方法的签名有时会尖叫,可能存在某种类型的null结果,并且使用Optional无论如何都不会添加任何语义。 例如:

 public Point2D intersect(Line line) 

或者在按键查找地图中的值的方法中。

如果一个人调用这样的方法并对结果执行空检查,那么很明显发生了什么(检查并行线路或未找到的密钥)。 我赞成返回null,以免我膨胀我的代码。

我总是会在javadoc中记录它。