【问题标题】:Is there a standard java exception class that means "The object was not found"?是否有一个标准的 java 异常类表示“找不到对象”?
【发布时间】:2014-09-05 18:33:29
【问题描述】:

考虑以下一般形式的函数:

Foo findFoo(Collection<Foo> foos, otherarguments)
throws ObjectNotFoundException {
    for(Foo foo : foos){
        if(/* foo meets some condition*/){
            return foo;
        }
    }
    throw new ObjectNotFoundException();
}

例如,一个具体的案例是:

User findUserByName(Collection<User> users, String name)
throws ObjectNotFoundException {
    for(User user : users){
        if(user.getName().equals(name)){
            return user;
        }
    }
    throw new ObjectNotFoundException();
}

如果找不到对象,这些函数会抛出异常。为此,我可以创建一个自定义异常类(在示例中为ObjectNotFoundException),但我更喜欢使用现有的类。但是,我在标准 java 库中找不到任何具有此含义的异常类。你知道这里有没有可以使用的标准异常吗?

【问题讨论】:

  • IlleagalArgumentException ?
  • 而你自己提供的异常是三行代码,为什么还要坚持JDK提供呢?我不知道您还在使用哪些 API;例如,JPA 提供了“NoResultException”。
  • 我会选择:IllegalArgumentException,正如文档所说:Thrown to indicate that a method has been passed an illegal or inappropriate argument. 实际上正在发生的事情。
  • 恕我直言,最好创建与您的方法/类/层/应用程序的上下文相关的自定义异常,但如果您坚持可以使用NoSuchElementException
  • @Gimby OP 在什么时候“坚持”JDK 提供异常? OP 陈述了一个偏好并提出了一个问题。此外,三行代码在适当记录后会变得更长。 如果JDK已经提供了它,为什么还要麻烦呢?

标签: java exception standard-library


【解决方案1】:

对于 Java 8,我建议在此用例中使用 Optional。

Optional<User> findUserByName(Collection<User> users, String name){
    Optional<User> value = users
        .stream()
        .filter(a -> a.equals(name))
        .findFirst();
}

这也让调用者非常清楚,如果未找到该值,则可选项可以为空。如果实在想抛出异常,可以在Optional中使用orElseThrows来实现。

【讨论】:

    【解决方案2】:

    你知道这里有没有可以使用的标准异常吗?

    有几个例外可以使用(例如NoSuchElementExceptionIllegalArgumentException),但答案实际上取决于您打算传达的语义:

    • NoSuchElementException 往往在您单步执行序列或枚举时使用,在这里您需要进行查找。

    • IllegalArgumentException 往往暗示参数有误,但在这种情况下,可能是调用者的假设不正确,或者是特定于应用程序逻辑的东西。

    • 自定义异常允许您(在 javadocs 中)准确地说明异常的含义。你也可以声明它是checked ...如果合适的话。

    (但不要试图使用UnknownUserException。那将是非常错误的;请阅读 javadoc!)


    还值得考虑返回null,特别是如果查找失败可能是您的应用程序中相当常见(非异常)的事件。 然而,返回null 的缺点是调用者需要检查null 或冒着意外NullPointerExceptions 的风险。事实上,我认为过度使用null 比过度使用异常更糟糕。前者可能导致应用程序不可靠,而后者“只是”对性能不利。

    对于 Java 8 及更高版本,返回 Optional 比返回 null 更简洁。


    在这些事情上,重要的是要超越教条,并根据实际情况做出决定。

    【讨论】:

      【解决方案3】:

      IllegalArgumentException 有时在此处使用,但使用您自己的 Exception 完全没问题。

      顺便说一句,我建议使用以String name 作为键和User 作为值的映射。这样就不需要对集合进行迭代,并且会阻止集合中有两个同名的用户。如果您不想使用地图,那么至少要像这样防御NullPointerException

      User findUserByName(Collection<User> users, String name) throws ObjectNotFoundException
      {
        if (name == null)
        {
          throw new IllegalArgumentException("name parameter must not be null");
        }
        if (users == null)
        {
          throw new IllegalArgumentException("Collection of users must not be null");
        }
        for(User user : users)
        {
          if(name.equals(user.getName()))
          {
            return user;
          }
        }
        throw new ObjectNotFoundException("Unable to locate user with name: " + name);
      }
      

      【讨论】:

      • 迟到的评论。您是否应该“防御”null 和 NPE,这是值得商榷的。另一种观点是,当null 传递给未记录 允许null 的API 方法时,这是一个BUG,您应该 抛出NPE(作为该条件的最具体例外)。完全阻止 NPE(例如通过尝试“改善”)或抛出不同的异常将无济于事,并且可能通过隐藏应该引起开发人员注意的问题而使事情变得更糟/维护者。
      • 在这个例子中,如果你抛出 NullPointerExceptionIllegalArgumentException 没有任何区别。两者都是未经检查的异常,并且都应该导致调用者使用堆栈跟踪“纾困”以供开发人员查看。所以,至少users == null 检查是多余的。 (NPE 是你的朋友,而不是你的敌人!)
      【解决方案4】:

      这取决于您方法的文档化接口契约:

      如果您的方法文档声明 name 参数必须对应于现有用户的名称,那么如果找不到该名称,则抛出 IllegalArgumentException 是合适的,因为这意味着调用者通过传递与用户不对应的名称违反了方法的要求。

      如果您的方法没有说名称必须与现有用户相对应,那么传递未知名称不是错误,您根本不应该抛出异常。在这种情况下,返回 null 是合适的。

      请注意,您的 findUserByName 方法基本上是对 Map.get 方法的重新发明,如果未找到指定的密钥,该方法将返回 null

      【讨论】:

        猜你喜欢
        • 2012-08-31
        • 2013-04-01
        • 1970-01-01
        • 2015-05-04
        • 2017-05-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-02
        • 2011-05-24
        相关资源
        最近更新 更多