【问题标题】:Is IllegalStateException appropriate for an immutable object?IllegalStateException 是否适合不可变对象?
【发布时间】:2010-07-16 18:50:48
【问题描述】:

如果:

  1. 由于一个或多个字段的值,方法无法完成其工作
  2. 那些字段是final 并且只在构造函数中赋值?

教科书示例:你的类是不可变的Collection<BigInteger>,你的方法应该返回最大元素,但这个实例是空的。

我已阅读有关该主题的 Kevin Bourillon`s blog post,但不确定适用哪个规则。

UnsupportedOperationException - 这意味着调用的方法对于此类(具体类型)的实例将始终失败,无论该实例是如何构造的。

绝对不是。该类的许多实例不为空,操作会成功。

IllegalStateException - ...确实存在至少一个相关实例可能处于的备用状态,该状态将通过检查... ...另请注意,此异常是否适用不可能真正改变实例状态的这一方面,或者已经太晚了。

不完全是。这个实例是用零长度构造的,所以 这个实例 不是而且不可能是非空的。

IllegalArgumentException - 抛出此异常意味着该参数至少存在一个其他值会导致相关检查通过。

如果相关参数是隐式 this 参数,则可能适用。这是我很想抛出的异常,但我担心它可能会造成混淆。


更新:将示例从 Collection<Integer> 更改为 Collection<BigInteger>,因为存在标识元素 (Integer.MIN_VALUE) 的事实分散了问题的注意力。

【问题讨论】:

  • 这个问题有两个不同的东西。 One 是有一个实用方法,它返回给定集合的最大值(正如 Jon 指出的那样,就像 Collections.max 方法一样) other 在你的问题中标题,一个具有 max 方法的不可变对象,返回委托对象的最大值,或者来自他自己。第二种情况是我要回答的stackoverflow.com/questions/3267958/…

标签: java exception


【解决方案1】:

听起来您上面提到的任何常见异常类都不适合教科书示例。

您应该抛出一个NoSuchElementException,因为这正是Collections.max() 方法所做的。

【讨论】:

  • 我通常不喜欢使用像-1 这样的异常值,但在这种情况下 一个标识元素:Integer.MIN_VALUE。这只是表明我选择了一个不好的例子,所以我将编辑问题以引用 BigInteger 而不是 Integer :-)
  • +1 到 NoSuchElementException,但 return -1 是个坏主意。返回Integer.MIN_VALUE 也是如此,如果返回它仍然不会告诉您实际上是否存在最大值(仅包含 Integer.MIN_VALUE 的集合呢?空值是比两者更好的选择,但不如异常 IMO .
  • 顺便说一下,NoSuchElementException 也是 Iterator.next() 抛出的,如果你越过了终点。它们在概念上相似:我无法满足您的要求。
  • 啊!为应该返回最大值的方法返回 -1?啊啊啊!
  • +1 Collections.max,抛出那个,因为他无法控制用于创建不可变对象的参数,所以在创建对象时最好是 IllegalArgumentException。这正是 finnw 所描述的情况
【解决方案2】:

我认为IllegalStateException 在这里很合适。如果实例构造正确(即“已经为时已晚”部分),实例可能处于正确状态。

【讨论】:

  • 在这种情况下,它应该是非法参数异常,但是你意识到对象的构造不正确。
  • @OscarRyz 仅当它的任何方法都不能处理给定状态时才如此。如果 getMax() 是状态非法的唯一函数,我会说 IllegalStateException 是合适的。
  • ILM Mhh 是的,你说得对,但我宁愿使用 UnsupportedOperationException 但这很棘手..mmmhh 怎么样.. UnsopporteOperationCausedByIllegalStateOriginatedByIllegalArgumentException :)
【解决方案3】:

如果类的状态有效(空集合),则最大元素为空。如果状态无效,则应该在构造时抛出 IllegalArgumentException。

【讨论】:

    【解决方案4】:

    IllegalStateException 最接近您想要的:“无论是否可以实际改变实例状态的这一方面,此异常都是适当的”。

    Not UnsupportedOperationException,因为对于该类的某些实例它可能会成功,并且为(可能)不带参数的方法抛出 IllegalArgumentException 肯定会使人们感到困惑。

    【讨论】:

    • 但是不可变对象只有一个状态... :-/
    • 不,它只有一个可访问状态。请参阅我第一句话的第二个原因。
    【解决方案5】:

    IllegalStateException 是否适用于不可变对象?

    不,因为不可变对象只有一种状态,不能从一种合法状态转移到另一种合法状态。

    所以,你正在构造一个不可变对象,你的对象应该有一个 max 方法

    class YourObject {
        public BigInteger max(){ ... }
    }
    

    我这种情况IllegalAgumentException应该是正确的,但不是直到方法执行完毕,而是对象创建时!

    因此,在这种情况下,如果您有一个不可变的大整数集合,并且使用零元素创建它,那么您在创建集合时会收到“无效参数”,这时您必须抛出异常。

    我同意 Jon 的观点,如果您的用例或在您的分析中,您愿意支持其余的操作,您可以抛出 NoSuchElementException,但我认为这将推迟问题。最好首先避免创建对象。

    所以,抛出 IllegalArgumentException 就像:

      // this is my immutable object class
      final class YourObject {
          private final Collection<BigInteger> c;
          public YourObject( BigInteger ... values ) {
              if( values.length == 0 ) { 
                  throw new IllegalAgumentException("Must specify at least one value");
              }
              ... initialize the rest... 
          }
          public BigInteger max() {
              // find and return the max will always work 
          }
       } 
    

    客户:

       YourObject o  = new YourObject(); // throws IllegalArgumentException 
       // the rest is not executed....
       doSomething( o ) ; 
       ...
       doSomething( YourObject o ) {
            BigInteger maximum = o.max();
       }
    

    在这种情况下,您不需要检查 doSomething 中的任何内容,因为程序会在创建实例时失败,而这又会在开发时修复。

    抛出 NoSuchElementException 会像:

      final class YourObject {
          private final Collection<BigInteger> c;
          public YourObject( BigInteger ... values ) {
               // not validating the input  :-/ oh oh.. 
              ... initialize the rest... 
          }
          public BigInteger max() {
              if( c.isEmpty() ) { throw NoSuchElementException(); }
              // find and return the max will always work after this line
          }
       } 
    

    客户:

       YourObject o  = new YourObject(); // it says nothing
       doSomething( o ) ;
       ...
       doSomething( YourObject o ) {
            BigInteger maximum = o.max();// ooops!, why? what?...  
            // your object client will start questioning what did I do wrong
            // and chais of if( o != null && o.isEmpty() || moonPhaseIs... ) 
       }
    

    请记住,如果程序失败,您能做的最好的事情是to making it fail fast

    Collections.max 有不同的目的,因为作为一个实用方法(不是不可变对象),他不能对空集合的创建负责(发生这种情况时他不在场),他唯一能做的就是说 “这个集合中没有 max 这样的东西” 因此 NoSuchElementException。

    最后一点,RuntimeExceptions,should be used for programming mistakes only(可以通过在发布之前测试应用程序来修复)

    【讨论】:

      【解决方案6】:

      您应该抛出UnsupportedOpertationException,因为Java 标准库在相同情况下就是这样做的。您的示例是一个类型限定符对象协议。这个模式在"An Empirical Study of Object Protocols in the Wild"中定义:

      某些类型会在其生命周期内禁用某些方法 物体。在类型限定符类别中,对象实例将在构造时进入它永远不会离开的抽象状态 S。调用实例 方法m,如果在状态S被禁用,总是会失败。

      在您的示例中,您的对象进入了一个抽象状态,我将在构造时将其称为 EmptyCollection,并且它永远不会离开该状态,因为集合字段是 final。在 EmptyCollection 抽象状态下,所有对 getMax() 实例方法的调用将始终失败。

      Beckman 研究了寻找对象协议的开源 Java 程序,并对生成的类进行了分类。第三个最常见的协议类别,出现在 16.4% 的抽样协议中,是 类型限定符

      Beckman 的论文列出了许多类型限定符示例,我选择了其中三个,在每种情况下,不可用的方法都会抛出 UnsupportedOperationException

      1. 当您通过调用Colections.unmodifiableList(...) 创建一个不可修改的列表,然后在结果列表上调用add 方法时。
      2. 当您创建一个不受数组支持的java.nio.ByteBuffer 然后调用array 方法时。
      3. 当您创建一个不支持压缩的java.imageio.ImageWriteParam 然后调用setCompressionMode 方法时。

      请注意,这些示例遵循您引用的 Kevin Bourillon 的建议。这些方法的失败取决于实例的构造方式。在示例 1 和示例 2 中,成功和失败的实例可能属于不同的具体类,因为 ListByteBuffer 是抽象的。然而,ImageWriteParam 是一个具体的类,所以ImageWriteParam 的一个实例可能会抛出UnsupportedOperationException 而另一个可能不会。由于 Java 标准库的设计者也定义了异常类型,我将跟随他们的领导而不是 Bourillon 先生的。

      附:当你的对象的抽象状态可以在运行时改变时,你应该使用IllegalStateException。 Beckman 论文中其他 83.6% 的示例属于这种类型。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-06-14
        • 2013-04-24
        • 1970-01-01
        • 2015-11-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多