Java:声明可以抛出异常的接口方法的正确方法是什么?

时间:2011-06-15 17:28:06

标签: java exception-handling osgi

假设我有这个由多个供应商实现的接口A:

interface A
{
    void x();
    void y();
}

但是,我希望供应商能够抛出异常来发出信号失败的信号,并且该方法可能会抛出RuntimeException。在每种情况下,调用这些方法的代码都应该处理失败并继续。仅仅因为1个供应商抛出NPE,我不希望系统崩溃。我想通过将每个方法声明为:

来确保每个调用都能捕获所有异常,而不是将其留给调用方法的人(或者真正维护线路)。
void x() throws Exception;

但这通常是不好的做法(PMD不喜欢它,一般我同意具体方法的规则)所以我想这是规则的例外还是有更好的方法?

让我说清楚,我正在寻找一种解决方案,其中接口的调用者被迫处理所有异常(包括RuntimeException s)。

为了进一步详细说明我的环境,所有这些都在OSGi框架内运行。因此,每个供应商将其代码打包在一个捆绑包中,OSGi将处理所有异常以防止整个系统崩溃。我真正关注的是OSGi服务接口,它将被某些核心捆绑包调用。我想要确定的是,当我遍历所有服务时,一个服务不会抛出NPE并停止正在执行的进程。我希望通过捕获从服务中抛出的所有异常来更优雅地处理它,以便仍然管理其他提供的服务。

7 个答案:

答案 0 :(得分:7)

创建自己的Exception类即。 MySeviceException并将其从界面中抛出。这里的想法是抛出有意义的异常,所以如果为您的目的提供最大的可读性和可维护性,不要害怕创建许多自定义异常类。您可以在下游捕获供应商详细的异常并将它们包装为您的自定义异常,以便上游流不必处理特定于供应商的异常。

class MySeviceException extends Exception{
    public MySeviceException() {}  
    public MySeviceException(String msg) { super(msg); }  
    public MySeviceException(Throwable cause) { super(cause); }  
    public MySeviceException(String msg, Throwable cause) { super(msg, cause); } 
}

interface A
{
    void x() throws MySeviceExceptionException;
    void y() throws MySeviceExceptionException;
}

根据经验,永远不会抓住Errors,总是抓住Exceptions并处理它!

答案 1 :(得分:2)

供应商当然可以将RuntimeException抛给他们内心的内容,因为他们没有被检查。这意味着您不必将throws子句添加到接口。

这也意味着客户端不会从接口定义中发出任何警告,编译器也不会强制执行必需的try / catch。他们知道的唯一方法就是阅读你的Javadocss。

您可以创建扩展java.lang.Exception的自定义例外;这是一个经过检查的例外。你必须将它添加到throws子句;编译器将围绕这些方法强制执行try / catch;你的客户将不得不处理这个问题。

答案 2 :(得分:1)

由于您强制实施者的限制,我会避免在界面中放置异常抛出。如果我必须通过实现你的接口抛出异常,它将使测试的模型更加困难。

如果实现接口的代码出现问题,那应该是实现类程序员的关注。

答案 3 :(得分:1)

创建一个throws子句,即使使用自定义异常类型,也不是真正的答案。你总是会遇到像NPEs这样的东西。即使您指定供应商必须将所有异常包装在您的自定义异常类型中,也会出现某些人犯错并且NPE通过的情况。如果不是因为错误,你就不会有NPE。

在很多情况下添加“throws Exception”是一个坏主意,但在某些情况下它会解决。在像Struts和JUnit这样的框架中,您可以添加“throws Exception”而不会出现问题,因为框架允许它。 (如果抛出的第一个异常使测试套件停止,JUnit将不会非常有用。)

您可以设计系统,以便调用供应商代码发生在插入系统的特定模块中,并且系统负责处理异常处理,类似于Struts中的Actions工作方式。每个Action都可以抛出任何东西,有一个异常处理程序可以记录出错的地方。这样,调用供应商API的业务逻辑就不必被无用的异常捕获样板所困扰,但是如果出现问题,程序就不会退出。

答案 4 :(得分:1)

我同意PMD - 声明你抛出通用Exception是丑陋的。

我认为最好
  • 定义新的,特定于域的非运行时异常类(如果内置的不会这样做;如果匹配它们的语义,则始终坚持预定义的类),并在可以抛出它们的方法中声明它们< / LI>
  • 将运行时异常保持在最低限度。只有当你无法控制你的被叫方式时,它们才有意义,而其他任何事情都无法做到。如果你有任何理由期望它们被抛入你正在调用的代码中,那么尽早捕获它们并将其作为标准异常重新抛出(见上文)。

最新版本的Java允许您链接异常。例如,如果在使用外部库解析NullPointerException ex时获得File f,您可能希望捕获它并重新抛出它,例如new FileParsingException("error parsing " + f + ": " + ex.getMessage(), ex);

答案 5 :(得分:1)

根据您的JVM设置,任何方法都能够抛出RuntimeException,无论您是否将其声明为抛出异常。捕获/处理RuntimeExceptions通常是一种不好的做法。虽然在某些有限的情况下可能需要此行为,但RuntimeExceptions主要表示代码存在问题,而不是产品的使用情况。当然,捕获RuntimeExceptions的一个主要缺点(特别是如果你忽略它们)是你的系统中的东西可能会爆炸,你不知道它正在发生......然后突然之间,你的系统完全吐出无效的数据,或者因某些其他原因而崩溃,使得追查根本原因变得更加困难。

请参阅Sun / Oracle关于异常的教程

http://download.oracle.com/javase/tutorial/essential/exceptions/runtime.html

要回答这个问题,除非你确切知道你可以抛出的异常衍生物,否则你几乎不会抛出异常,尽管我对抛出通用异常类的用处有很大的疑虑,除非你只是关心注销堆栈跟踪等等,以便你知道它发生了吗?如果你试图在不知道它是什么类型的异常的情况下强有力地处理异常,那么你可能不会非常成功地正确处理它甚至是不够好。

答案 6 :(得分:1)

您可以在抽象类中实现您的接口,该类使用模板方法模式捕获并使用自定义异常包装RuntimeExceptions。但是,没有办法强制供应商使用抽象类(文档除外)。

class MySeviceException extends Exception{
    public MySeviceException() {}  
    public MySeviceException(String msg) { super(msg); }  
    public MySeviceException(Throwable cause) { super(cause); }  
    public MySeviceException(String msg, Throwable cause) { super(msg, cause); } 
}

interface A
{
    void x() throws MySeviceExceptionException;
    void y() throws MySeviceExceptionException;
}

class ABase implements A
{
    public final void x() throws MySeviceExceptionException {
        try {
            doX();
        } catch(RuntimeException ex) {
            throw new MySeviceExceptionException(ex);
        }
    }
    public final void y() throws MySeviceExceptionException {
        try {
            doY();
        } catch(RuntimeException ex) {
            throw new MySeviceExceptionException(ex);
        }
    }

    public abstract void doX() throws MySeviceExceptionException;
    public abstract void doY() throws MySeviceExceptionException;
}