【问题标题】:When to throw IllegalStateException vs IllegalArgumentException?何时抛出 IllegalStateException 与 IllegalArgumentException?
【发布时间】:2018-07-22 00:34:46
【问题描述】:

让我们从 Javadocs 开始:

IllegalStateException

表示方法已被非法或不适当地调用 时间。换句话说,Java 环境或 Java 应用程序不是 处于请求操作的适当状态。

IllegalArgumentException

抛出表明一个方法已经被非法或 不恰当的论点。

上面的问题是它们非常黑白。考虑一个方法正在解析调用者提供的文件的用例。该文件存在、可读且格式正确。但是,文件中的某些内容不符合业务规则。在这种情况下抛出什么合适的异常 - IllegalStateException 或 IllegalArgumentException?

查看提供断言的各种库,例如 Guava Preconditions 或 Spring Assert,似乎没有达成共识。有一些很好的讨论 here 和 here,但没有一个对我上面提到的常见用例提供结论性的答案。

【问题讨论】:

  • 很抱歉,对此没有正确答案。做到这一点的“正确”方式是见仁见智......正如下面矛盾的答案和不合时宜的投票模式所清楚说明的那样。显然,没有“决定性”的答案。
  • (FWIW,我的观点是“两者都不是”......在这种情况下。但我同意那些说这两个例外是指正交事物的人。一个是关于通过的论点。另一个是关于调用时目标对象的状态(不是参数)。区别很明确。)

标签: java exception illegalstateexception illegalargumentexception


【解决方案1】:

换句话说:

IllegalArgumentException 在接受类型但不接受值的情况下被抛出,例如期望正数而您给出负数。

IllegalStateException 在不应该调用方法时抛出,例如从死线程调用方法。

我不明白它们如何混合。在您关于有问题的文件的问题中,我认为抛出ParseException 或IOException 会更合适。

【讨论】:

  • “我认为抛出 ParseException 或 IOException 会更合适” +1
  • 肯定不是IOException,因为IO操作没有错。 IMO,大多数 Java 库滥用IOException。你提到的ParseException是什么-JDK 8没有RuntimeException这样的东西。
  • @AbhijitSarkar java.text.ParseException
  • 好的,ParseException 表面上看起来很有希望,但在查看 Javadoc 之后,它似乎打算与流解析器一起使用。构造函数需要一个偏移量,我认为我不想跟踪它。我倾向于继承IllegalFormatException。
  • @MadProgrammer,我绝对建议不要使用IOException,因为 IO 操作没有任何问题,但不确定ParseException。 IllegalFormatException 的子类是更好的选择,我猜
【解决方案2】:

考虑一个用例......

如果这是我仅有的两个选项,在你的用例中,我倾向于IllegalStateException

为什么?因为参数是有效的,它们指向一个可以读取的文件。无效的不是参数,而是解析文件会使状态无效的事实。

这当然假设 IllegalStateException 和 IllegalArgumentException 是您正在考虑的唯一例外。

这当然只是 MHO。我认为重要的方面是,您可以以一致的方式定义使用一个异常而不是另一个异常的原因,从而使您的 API 易于理解。

我也同意 Saclyr(他们的回答 +1),您可以使用更合适的例外来定义方法调用失败的原因(个人认为java.text.ParserException)

【讨论】:

  • IllegalStateException 是在状态已经无效的时候,而不是在它将无效的时候。外部文件不是应用程序状态,因此这将是 IMO 的误导性异常。
  • @JohnKugelman 我并不反对,我专注于问题的 if-or 条件 - 可以使用更合适的例外情况。恕我直言,我仍然认为它提供了一个机会来描述应用属性会使对象的状态无效但输入/参数本身有效的情况。我没有将这个概念视为“一成不变”,而是根据可用的输入中断了意图。对我来说,正如我所说,两者都不适合用例
【解决方案3】:

IllegalStateException 用于编码错误,而不是输入错误。它用于违反类的不变量,或者当对象处于错误状态时调用方法。例如使用关闭的资源,或关闭资源两次。

IllegalArgumentException 是指根据方法 API 的参数具有无效值。当只允许正数时传递 -1。

在这种情况下,任何例外都是不合适的。我会创建一个IOException 的子类,因为输入文件中有错误。

【讨论】:

  • 恕我直言,我认为没有必要扩展 IOException,因为问题不在于文件,而是在解析文件时,ParseException 会更合适
  • 我同意@MadProgrammer,IOExceptions 表示 io 操作失败,但事实并非如此。此外,用例文件的格式很好,所以我不会使用ParseException,因为它会向开发人员发出格式无效的信号,而不是发出违反业务规则的信号。
【解决方案4】:

我也认为这两种方法的语义非常接近。

根据IllegalArgumentException javadoc,传递无效参数可以通过抛出IllegalArgumentException 来处理:

抛出表明一个方法被传递了一个非法的或 不恰当的论点。

但是调用带有错误参数的方法也可以通过抛出 IllegalStateException 来处理,因为它的 javadoc 指出:

表示方法已被非法或不适当地调用 时间。

确实,使用不适当或非法的参数调用方法可能也意味着该方法是在非法或不适当的时间调用的。

为了简单起见,我认为 IllegalArgumentException 和 IllegalStateException 可能会被一些开发人员以可互换的方式使用,因为问题是由传递的参数引起的。我上面解释的。
而与传递的参数无关的IllegalStateException 用例不必与IllegalArgumentException 互换。

细微差别很小,大多数库有时会混合使用它们。

恐怕不能给你一个更扎实的解释。 语义是语义性的,由于可以更接近地解释两件事,因此通常没有明确地使用它。

【讨论】:

  • @Downvoter 如果你不赞成重言式,你是否也赞成自我矛盾?如果这么多第三方库以不同的方式使用它们,这不仅仅是机会。
  • 我没有对你投反对票,但我可能会考虑。您的回答对我的问题中已经说明的内容没有任何帮助。它不应该作为答案发布。
  • “你的回答没有增加任何东西”我进入你的方式,但我添加了一件我认为重要的事情:我解释了为什么这些例外有时以可互换的方式使用。
【解决方案5】:

其他答案突出显示何时使用IllegalArgumentException 或IllegalStateException。但在我看来(注意:基于意见)这些例外不应在您的用例中使用。

总结:某些文件包含有效格式的数据,已成功加载到应用程序中,但某些值不符合您的业务规则(注意:没有 IO 操作失败,格式有效 => 既不是 IOException 也不是应该使用ParseException,它们表示IO操作失败或格式无效)。

为什么不应该使用IllegalArgumentException?

抛出此异常表示向方法传递了非法或不适当的参数。您可能会争辩说您有一种方法可以验证文件,并且该文件中的字段值或多个字段的值的组合是非法的或不符合您的业务规则。是的,指向你。但是,如果您在这种情况下抛出IllegalArgumentException,则无法将由其他库(或标准库或您自己的代码在其他地方)引起的IllegalArgumentExceptions 和来自您的验证器的IllegalArgumentExceptions 分开,这表明业务规则很容易违反(当然,您可以继承 IAE 并在调用方法中捕获它)。

为什么要分离这些异常?用例:应向用户显示违反业务规则的情况,以便他可以更改其不合规的输入。其他 IAE 或一般任何未捕获的运行时异常表明请求在服务器上失败,例如。在这些情况下,您必须向客户端发送不同的响应。


您可以以类似的方式争论为什么不应该使用IllegalStateExceptions 来指示违反业务规则。那么,在您的用例中应该使用什么? 这在很大程度上取决于您的应用程序的规模。 RuntimeException 的一些自定义子类可能会为小型应用程序完成这项工作。对于较大的应用程序验证库,如“javax.validation”值得一试。

【讨论】:

  • 您的回答很好地解释了一些细微差别,但同时看起来很像没有一个词的彻底研究。我的意思是 OP 已经在选择异常类型时感到困惑。我相信,根据 OP 的用例陈述您的意见会使它更像是一个答案,而不是对问题的扩展))
猜你喜欢
  • 2014-09-18
  • 2014-11-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多