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

时间:2010-07-16 18:50:49

标签: java exception

如果符合以下情况,您会抛出IllegalStateException

  1. 由于一个或多个字段的值
  2. ,方法无法完成其工作
  3. 这些字段是final并仅在构造函数中分配?
  4. 教科书示例:您的类是不可变的Collection<BigInteger>,并且您的方法应该返回最大元素,但此实例为空。

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

      

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

    绝对不是。这个类的许多实例都不是空的,操作也会成功。

      

    IllegalStateException - ...确实存在至少一个有问题的实例可能已进入的备用状态,它将通过检查...&lt; snip&gt; ...另请注意,无论是否可以实际改变实例状态的这一方面,或者已经太晚,这个例外都是合适的。

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

      

    IllegalArgumentException - 抛出此异常意味着此参数至少存在一个其他值会导致问题通过。

    如果相关参数是隐式this参数,则可以应用。这是我想要抛出的例外,但我担心它可能会令人困惑。


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

6 个答案:

答案 0 :(得分:11)

这听起来不像上面提到的任何常见异常类都适合Textbook示例。

你应该抛出一个NoSuchElementException,就像Collections.max()方法那样。

答案 1 :(得分:4)

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

答案 2 :(得分:3)

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

答案 3 :(得分:3)

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

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

答案 4 :(得分:2)

  

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(可以通过在发布之前测试应用程序来解决这些问题)

答案 5 :(得分:2)

你应该抛出一个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方法。
  4. 请注意,这些示例 not 遵循Kevin Bourillon建议您引用。这些方法的失败取决于实例的构造方式。在示例1和示例2中,成功和失败的实例可能具有不同的具体类,因为ListByteBuffer是抽象的。但是,ImageWriteParam是一个具体类,因此ImageWriteParam的一个实例可能抛出UnsupportedOperationException而另一个实例可能不抛出IllegalStateException。由于Java标准库设计者还定义了异常类型,我将遵循他们的领导而不是Bourillon先生。

    P.S。您应该使用{{1}}而不是对象的抽象状态可以在运行时更改。贝克曼论文中其他83.6%的例子属于这种类型。