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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6361736/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 15:34:40  来源:igfitidea点击:

Java: What is the proper way to declare an interface method that can throw an exception?

javaexception-handlingosgi

提问by Dave H

Let say I have this interface A that is implemented by multiple vendors:

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

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

However, I want vendors to be able to throw exceptions to signal something has failed and potentially the method could thrown a RuntimeException. In each case, the code that calls these methods should handle the failure and continue. Just because 1 vendor throws an NPE, I don't want the system to come crashing down. Instead of leaving it up to the person calling the method (or really down the line maintainence), I would like to make sure each call will catch all exceptions by declaring each method as:

但是,我希望供应商能够抛出异常来表示某些事情失败了,并且该方法可能会抛出一个RuntimeException. 在每种情况下,调用这些方法的代码都应该处理失败并继续。仅仅因为 1 个供应商抛出 NPE,我不希望系统崩溃。与其将它留给调用方法的人(或真正的维护),我想通过将每个方法声明为确保每个调用都能捕获所有异常:

void x() throws Exception;

but this is generally bad practice (PMD doesn't like it and generally I agree with the rule for concrete methods) so I wonder is this an exception to the rule or is there a better way?

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

Let me be clear, I'm looking for a solution where the caller of the interface is forced to handle all exceptions (including RuntimeExceptions).

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

To further detail my environment, all of this is running within an OSGi framework. So each vendor packages their code in a bundle and OSGi will handle all exceptions to prevent the entire system from crashing. What I'm really looking at are OSGi service interfaces that will be called by some core bundle. What I want to make sure is that when I iterate through all of the services, one service doesn't throw an NPE and stop the process that is executing. I want to handle it more gracefully by catching all exceptions thrown from the service so the other provided services are still managed.

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

回答by CoolBeans

Create your own Exception class ie. MySeviceExceptionand throw it from the interface. The idea here is to throw meaningful exceptions so don't be afraid of creating many custom exception classes if that provides most readability and maintainability for your purposes. You can catch the vendor detailed exceptions in the downstream and wrap them as your custom exception so that the upstream flow does not have to deal with vendor specific exceptions.

创建您自己的异常类,即。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;
}

As a rule of thumb never catch Errors, always catch Exceptionsand deal with it!

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

回答by duffymo

Vendors can certainly throw RuntimeException to their heart's content, because they're unchecked. That means you don't have to add the throwsclause to the interface.

供应商当然可以随意抛出 RuntimeException,因为它们是未经检查的。这意味着您不必将throws子句添加到接口中。

It also means that clients won't have any warning from the interface definition, and the compiler won't enforce a required try/catch. The only way they'll know is to read your Javadocss.

这也意味着客户端不会收到来自接口定义的任何警告,并且编译器不会强制执行所需的 try/catch。他们知道的唯一方法是阅读您的 Javadocss。

You can create a custom exception that extends java.lang.Exception; that's a checked exception. You'll have to add it to the throws clause; the compiler will enforce a try/catch around those methods; your clients will have to handle the problem.

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

回答by Brad Gardner

I would avoid placing the exception throw in the interface because of limitations that you force on implementers. If I must throw an exception by implementing your interface, it will make mockups for testing more difficult.

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

If something goes wrong in the code that implements your interface, that should be the concern of the programmer of the implementing class.

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

回答by Nathan Hughes

Creating a throws clause, even with a custom exception type, is not really an answer here. You are always going to have things like NPEs. Even if you specify to the vendors that all exceptions must be wrapped in your custom exception type there are going to be cases where somebody makes a mistake and an NPE gets through. If it wasn't for mistakes you wouldn't have NPEs.

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

Adding "throws Exception" is a bad idea in a lot of cases, but in some cases it works out. In frameworks like Struts and JUnit you can add "throws Exception" without a problem because the framework allows for it. (JUnit wouldn't be very useful if the first exception thrown made the test suite halt.)

在很多情况下,添加“抛出异常”是一个坏主意,但在某些情况下它会奏效。在像 Struts 和 JUnit 这样的框架中,您可以毫无问题地添加“抛出异常”,因为该框架允许这样做。(如果抛出的第一个异常导致测试套件停止,JUnit 将不会很有用。)

You could design the system so that calls to vendor code happen in specific modules that get plugged into the system and which have exception-handling taken care of by the system, similar to how Actions work in Struts. Each Action can throw anything, there's an exception handler that logs what went wrong. That way the business logic that calls into vendor APIs wouldn't have to be bothered with useless exception-catching boilerplate, but if something goes wrong the program doesn't exit.

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

回答by tucuxi

I agree with PMD - declaring that you throw generic Exceptions is ugly. I think it is better to

我同意 PMD - 声明你抛出泛型Exceptions 是丑陋的。我认为最好

  • Define new, domain-specific, non-runtime exception classes (if the built-in-ones will not do; always stick to predefined classes if you match their semantics), and declare them in the methods that can throw them
  • Keep runtime exceptions to a bare minimum. They only make sense when you have no control over how you are called, and nothing else will do. If you have any reason to expect them to be thrown in a piece of code you are calling, then catch them as early as makes sense and re-throw them as standard exceptions (see above point).
  • 定义新的、特定于域的、非运行时异常类(如果内置的不这样做;如果匹配它们的语义,总是坚持使用预定义的类),并在可以抛出它们的方法中声明它们
  • 将运行时异常保持在最低限度。只有当您无法控制自己被调用的方式时,它们才有意义,其他什么都做不了。如果您有任何理由期望它们在您正在调用的一段代码中被抛出,那么尽早捕获它们并作为标准异常重新抛出它们(见上点)。

Recent versions of Java allow you to chain exceptions. For example, if you get a NullPointerException exwhile parsing a File fwith an external library, you would want to catch it and rethrow it as, say, a new FileParsingException("error parsing " + f + ": " + ex.getMessage(), ex);

最新版本的 Java 允许您链接异常。例如,如果您用外部库NullPointerException ex解析 aFile f有一段时间,您会想要捕获它并将其重新抛出,例如,new FileParsingException("error parsing " + f + ": " + ex.getMessage(), ex);

回答by devyndraen

Depending on your JVM settings, any method has the ability to throw a RuntimeException whether you declare it as throwing Exception or not. Catching/handling RuntimeExceptions is generally a bad practice. While there are some limited cases where this behavior could be required, RuntimeExceptions are primarily an indicator that there is something wrong with the code, rather than the usage of the product. Of course, a major downside to catching RuntimeExceptions (especially if you're ignoring them) is that things could be blowing up in your system and you have no idea that it's happening...then all of a sudden, your system spits out completely invalid data, or crashes anyway from some other reason, making it more difficult to track down the root cause.

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

See Sun/Oracle's tutorial about exceptions

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

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

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

To answer the question, unless you know exactly what Exception derivative you can expect to be throwing, you're pretty much stuck throwing Exception, though I have major doubts as to the usefulness of throwing the generic Exception class, unless you only care about logging out the stack trace and such so that you know it happened? If you're trying to robustly handle the Exception without knowing what kind of Exception it is, then you probably won't be very successful in handling it properly or even well-enough.

要回答这个问题,除非您确切地知道可以期望抛出什么 Exception 派生类,否则您几乎无法抛出 Exception,尽管我对抛出通用 Exception 类的用处有很大的怀疑,除非您只关心日志记录出堆栈跟踪等,以便您知道它发生了?如果您试图在不知道它是什么类型的异常的情况下稳健地处理异常,那么您可能不会非常成功地正确处理它甚至足够好。

回答by Ryan Gross

You could implement your interface in an abstract class that uses the template method pattern to catch and wrap RuntimeExceptions with a custom exception. However, there is no way to enforce that the vendors use the abstract class (other than documentation).

您可以在抽象类中实现您的接口,该抽象类使用模板方法模式来捕获并使用自定义异常包装 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;
}