让构造函数引发异常是好的做法吗?

2022-08-31 06:19:45

让构造函数引发异常是一种好的做法吗?例如,我有一个类,我是唯一的属性。现在我将类作为Personage

class Person{
  int age;
  Person(int age) throws Exception{
   if (age<0)
       throw new Exception("invalid age");
   this.age = age;
  }

  public void setAge(int age) throws Exception{
  if (age<0)
       throw new Exception("invalid age");
   this.age = age;
  }
}

答案 1

在构造函数中引发异常并不是一种坏做法。事实上,这是构造函数指示存在问题的唯一合理方法;例如,参数无效。

我还认为,抛出已检查的异常是可以的1,假设已检查的异常是 1) 声明的,2) 特定于您报告的问题,以及 3) 期望调用方处理此2 的已检查异常是合理的。

然而,明确声明或投掷几乎总是不好的做法。java.lang.Exception

应选择与已发生的异常情况匹配的异常类。如果抛出,则调用方很难将此异常与任意数量的其他可能的声明和未声明的异常分开。这使得错误恢复变得困难,如果调用方选择传播异常,问题就会蔓延。Exception


1 - 有些人可能不同意,但IMO在这种情况下与在方法上抛出异常的情况之间没有实质性的区别。标准检查和未检查建议同样适用于这两种情况。
2 - 例如,如果您尝试打开不存在的文件,现有的 FileInputStream 构造函数将引发 FileNotFoundException。假设 FileNotFoundException 是已检查的异常3 是合理的,则构造函数是引发该异常的最合适位置。如果我们在第一次进行读取写入调用时抛出FileNotFoundException,则可能会使应用程序逻辑更加复杂。
3 - 鉴于这是检查异常的激励示例之一,如果您不接受这一点,那么您基本上是在说应该取消选中所有异常。这是不切实际的...如果你打算使用Java。


有人建议使用用于检查参数。这样做的问题是,可以通过 JVM 命令行设置打开和关闭断言的检查。使用断言来检查内部不变量是可以的,但是使用它们来实现javadoc中指定的参数检查不是一个好主意......因为这意味着您的方法只有在启用断言检查时才会严格实现规范。assertassert

第二个问题是,如果一个断言失败了,那么就会被抛出,并且得到的智慧是,试图捕获它和它的任何子类型都是一个坏主意assertAssertionErrorError


答案 2

我一直认为在构造函数中抛出已检查的异常是不好的做法,或者至少是应该避免的。

这样做的原因是您无法执行此操作:

private SomeObject foo = new SomeObject();

相反,您必须执行此操作:

private SomeObject foo;
public MyObject() {
    try {
        foo = new SomeObject()
    } Catch(PointlessCheckedException e) {
       throw new RuntimeException("ahhg",e);
    }
}

在我构建SomeObject的时候,我知道它的参数是什么,那么为什么我应该期望我把它包装在一个try catch中呢?啊,你说,但如果我从动态参数构造一个对象,我不知道它们是否有效。好吧,你可以...在将参数传递给构造函数之前对其进行验证。这将是很好的做法。如果您只关心参数是否有效,那么您可以使用TrafreArgumentException。

因此,与其抛出已检查的异常,不如执行

public SomeObject(final String param) {
    if (param==null) throw new NullPointerException("please stop");
    if (param.length()==0) throw new IllegalArgumentException("no really, please stop");
}

当然,在某些情况下,抛出一个已检查的异常可能是合理的。

public SomeObject() {
    if (todayIsWednesday) throw new YouKnowYouCannotDoThisOnAWednesday();
}

但这种可能性有多大呢?