返回NULL的替代方法

时间:2011-05-12 16:19:40

标签: java design-patterns null return-value

   /**
     * 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;
    }

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

感谢。

修改

根据 id 更改代码以获取Foo,以更好地说明真实场景。

15 个答案:

答案 0 :(得分:20)

返回null不仅更易于处理,而且效果也更好。必须使用例外来处理例外案例。

答案 1 :(得分:11)

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

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

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

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

嗯...当有疑问时,错误地支持null:)

答案 2 :(得分:5)

如果将null记录为有效结果,则返回Foo即可。

另一种选择是空对象模式。那是 - public class Foo { public static final Foo NULL_FOO = new Foo(); } 的一个没有任何数据的实例:

{{1}}

然后返回。

答案 3 :(得分:4)

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

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

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

答案 4 :(得分:4)

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

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

你也可能有'收藏中是否存在这个?'返回bool的方法,如果你没先检查,则从上面的代码中抛出异常。


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

答案 5 :(得分:3)

最好的做法是在Javadoc中说当找不到匹配时null会返回。

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

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

我宁愿只返回null。

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

答案 6 :(得分:1)

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

答案 7 :(得分:1)

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

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

答案 8 :(得分:1)

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

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

答案 9 :(得分:1)

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

答案 10 :(得分:1)

这可能不会直接回答您的问题,但它来自Stackoverflow成员在类似主题上的一些评论。

  

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

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

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

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

答案 11 :(得分:1)

这是一个古老的问题,但我没有找到这里提到的番石榴Optional课程以及JDK的Optional(来自Java 8),它也提供相同的服务目的并具有更多功能。

This article很好地概述了使用番石榴的可选原因。我强烈建议你阅读它。

这是一段摘录:

  

重点是什么?

     

除了提供null a的可读性增加之外   名称,Optional的最大优点是它的白痴证明。它   如果你想要你的话,强迫你积极思考缺席的情况   程序要编译,因为你必须积极打开   可选并解决该案例。 Null令人不安地容易   简单地忘记了事情,尽管FindBugs有所帮助,但我们并不这么认为   几乎也解决了这个问题。

     

当您返回可能或的值时,这尤其重要   可能不会出现。"你(和其他人)更容易忘记   other.method(a,b)可以返回一个空值,而不是你可能的   忘记当你实现other.method时a可能为null。   返回Optional使得呼叫者无法忘记这一点   因为他们必须自己解开对象的代码   编译。

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

public Point2D intersect(Line line)

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

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

我总是会在javadoc中记录它。

答案 12 :(得分:0)

我会说这取决于您的应用以及该方法的使用方式。

  1. 如果您认为“未找到”结果经常发生,可能会对性能产生负面影响*,则返回null,并在调用者中检查它。
  2. 如果“未找到”结果将相对罕见或将导致您的应用程序放弃请求,那么抛出异常就可以了。例外可以使您的代码更容易阅读,因为您可以保证成功返回将生成一个对象,因此您不必对其进行空值检查。
  3. 我赞同这样的观点,即异常应该用于例外条件,这是错误条件的超集。我还采用了Spring / Hibernate对未经检查的异常的偏好,这避免了繁琐的try / catch逻辑。

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

答案 13 :(得分:0)

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

list是否包含null的{​​{1}}个?如果是这种情况,则不会知道是否找到了ID,或者它是否与Foo相关联。在这种情况下:

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

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

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

一般来说,我可以说:

  • ID 未找到例外吗?使用existsFoo;
  • 结果是否传递给其他类,用作对象?使用包装纸,例如IllegalArgumentException;
  • 只检查SearchResult是否为空(是否存在)?使用其他方法getFoo

答案 14 :(得分:0)

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

注释excerpt below将帮助您不要陷入旧的方式......

  

使用@Nullable

     

要消除代码库中的NullPointerExceptions,您必须这样做   关于空引用的纪律。我们在这方面取得了成功   遵循并执行一个简单的规则:

     

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

     

Guava:适用于Java和JSR-305的Google核心库具有简单的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。