我应该在方法签名中声明所有从方法中抛出的异常,还是只声明异常的超类?

3
当我从一个方法中抛出已检查的异常时,我应该在方法签名中只声明异常的超类还是所有不同类型的异常都要声明?如果我有以下异常:
private class SuperException extends Exception {

}

private class SubExceptionOne extends SuperException {

}

private class SubExceptionTwo extends SuperException {

}

方法签名应该是什么?
 void confirmAccount() throws SubExceptionOne, SubExceptionTwo;

或者
 void confirmAccount() throws SuperException;

在最后的方法签名中,我要怎样告诉其他开发者这个方法可能会抛出哪些异常?如果不同的子类型需要不同的处理呢?

1
这要看情况。按照同样的逻辑,你可以使用声明 throws Exception。有多少个子类?你想传达什么信息?例如,通常会声明 throws IOException 而不是可能抛出的所有子类。 - Boris the Spider
请参见 https://dev59.com/Z33aa4cB1Zd3GeqPj_Pj。 - Raedwald
3个回答

2
接口应尽可能稳定。因此,"Super"策略可能更好。许多库使用"Super"策略,因为异常规格在可维护性上比可读性或安全性带来更多的烦恼。即使是IOException也是一个几乎所有Java库代码都使用的Super,而不是声明更具体的异常。(但当它们声明更具体的异常时,这是因为合同规定不会抛出更一般的IOExceptions。请继续阅读。)
如果您真的想说每个子类异常都可能被抛出,但不想说任何派生类都可能被抛出,那么您可以列出Sub1和Sub2。也许Sub1是NumberCrunchException,并且您的方法调用了crunchNumbers(),使用您的方法的用户可以确保这是您的方法唯一会抛出的异常。在这种情况下,具体的策略更好。

如果你只声明了超类,那么调用者应该如何捕获异常呢?只能通过超类型吗?还是他们需要知道可以抛出的超类型的子类型,然后捕获符合条件的子类型?我的意思是,调用者可能想根据抛出的异常做不同的事情。如果你只抛出超类型,那么你是否需要在JavaDoc中说明这一点呢? - LuckyLuke
2
@LuckyLuke,回答你的问题,请看一下新的Java Files API。是的,你可以抛出超类并在Javadoc中描述所有可能的异常和错误情况。调用类可以决定如何处理这些信息。 - Boris the Spider
@LuckyLuke 如果你的方法确实可以抛出一个原始的Super,但也可以抛出Sub1,那么问题就被迫出现了。然而,如果你将你的Super声明为抽象的,我会说这是完全合理的设计,可以声明具体内容并捕获具体内容...在这种情况下,你为什么还要继承自Super呢? - djechlin
@djechlin 谢谢您的答案。我目前有一个ValidationException,它是一些更具体的异常类的超类,例如DuplicateUsernameException,PasswordTooWeakException等。这些异常需要客户端进行不同的处理。如果我像新文件API那样做,似乎很好。 - LuckyLuke

1
如果不同的子类型需要不同的处理方式,则一定要声明两种不同的异常。永远不要期望开发人员在使用你的方法时猜测你实际上正在抛出不同类型的异常。
如果你声明了两个不同的异常,并且用户从Javadoc中知道它们实际上是同一个类的子类,用户可以选择使用catch(SuperException e)来捕获它们,而不是两个单独的catch子句。但这取决于用户的选择。
如果你不单独声明它们,你的IDE不会在Javadoc注释中添加适当的@Throws。因此,你的Javadoc只会指示你抛出了SuperException,这会使用户感到困惑。通过将其放在评论文本中来解决问题并不是真正的解决方案。如果任何工具使用反射确定你的方法抛出了什么异常,它将无法看到从Method.getExceptionTypes()返回的数组中的单个异常。
如果不同的异常所期望的功能大体相同,只是它们在日志中的显示方式不同,那么最好只使用父异常,而使用不同的消息。

不需要。catch块会自动执行instanceof检查。接收方将决定是否需要以不同的方式检查特定子类型(例如,先捕获FileNotFoundException然后才是IOExcpetion)。这是关于传达信息的。 - Boris the Spider
如果@BorisTheSpider声明了他的方法会抛出IOException,那么catch子句将如何决定他是抛出了FileNotFoundException还是EOFException。编译器在try中没有检测到任何抛出这些特定异常的方法。 - RealSkeptic
接收器将首先捕获他们想要处理的任何特定异常,然后声明一个catch来捕获超类。try...catch将按顺序查看catch是否与抛出的异常匹配 - 因此编译器仅要求在某个地方捕获IOException。如果您一直在catch块中使用instanceof,那么肯定出了很大的问题... - Boris the Spider
@BoristheSpider 好的,我已经删除了关于instanceof的部分,但是我的观点仍然存在:你不应该让用户猜测你实际上正在抛出不同类型的异常。 - RealSkeptic
我认为,抛出异常的类并不应该决定如何处理异常。整个重点在于,某些通用的IO类可能会声明它抛出一个“IOException”。例如,处理文件重命名的类可能会抛出“FileNotFoundException”。 - Boris the Spider
@BoristheSpider 一般的IO类实际上会抛出一个真正的IOException,而不是它的子类。如果它们决定要通用,应该实际上抛出它们声明的类。如果它们决定要具体并允许用户区分两种不同的错误状态,则应声明它们正在抛出什么。 - RealSkeptic

0

throws子句的作用是向调用方法传递有关在调用此方法期间可能出现什么问题的有用信息。这意味着您需要根据要传达的信息量来决定具体程度,而这将取决于应用程序。

例如,声明throws Exception几乎总是不明智的:它所传达的信息只是“可能会出现问题”,这太模糊了,无法提供有用的信息。但是,是否需要在throws子句中提供完全细粒度的信息取决于您查看程序时需要调用的类。没有固定的答案。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接