有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

如果出现以下情况,您是否会抛出IllegalStateException

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

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

我已经阅读了关于这个主题的Kevin Bourillon`s blog post,我不确定哪条规则适用

UnsupportedOperationException - this means that the method invoked will always fail for an instance of this class (concrete type), regardless of how the instance was constructed.

绝对不是。该类的许多实例都不是空的,并且该操作本应已成功

IllegalStateException - ... there does exist at least one alternate state that the instance in question could have been in, which would have passed the check ... <snip> ... Note also that this exception is appropriate whether or not it is possible to actually mutate this aspect of the instance's state, or it's already too late.

不完全是。此实例是用零长度构造的,因此此实例不是且永远不可能是非空的

IllegalArgumentException - throwing this exception implies that there exists at least one other value for this parameter that would have caused the check in question to pass.

如果所讨论的参数是隐式this参数,则可能适用。这是我想抛出的一个例外,但我担心这可能会令人困惑


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


共 (6) 个答案

  1. # 1 楼答案

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

    不是UnsupportedOperationException,因为它可能会在该类的某个实例中成功,而为(可能)不带参数的方法抛出IllegalArgumentException肯定会让人困惑

  2. # 2 楼答案

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

  3. # 3 楼答案

    听起来你上面提到的任何常见异常类都不适合教科书中的例子

    您应该抛出一个^{},因为这正是^{}方法所做的

  4. # 4 楼答案

    如果类的状态是有效的(空集合),max元素就是null。如果状态无效,则应在构造时引发IllegalArgumentException

  5. # 5 楼答案

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

    Some types disable certain methods for the lifetime of the object. In the type qualifier category, an object instance will enter an abstract state S at construction-time which it will never leave. Calls to an instance method m, if it is disabled in state S will always fail.

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

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

    Beckman的论文列出了许多类型限定符示例,我选择了其中三个,在每种情况下,unavailable方法都抛出一个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。贝克曼论文中的其他83.6%的例子属于这种类型

  6. # 6 楼答案

    Is IllegalStateException appropriate for an immutable object?

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

    因此,您正在构造一个不可变对象,并且您的对象应该有一个max方法

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

    我认为这种情况下IllegalAgumentException应该是正确的,但不是在执行方法之前,而是在创建对象时

    因此,在这个场景中,如果您有一个不可变的BigInteger集合,并且您使用零元素创建它,那么在创建集合时您将收到一个“无效参数”,此时您必须抛出异常

    我同意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这样的东西”,因此没有任何uchelementexception

    最后一句话,RuntimeExceptions,should be used for programming mistakes only(那些可以通过在发布应用程序之前测试它来修复的异常)