【问题标题】:Using interface hides the exception thrown by implementation使用接口隐藏实现抛出的异常
【发布时间】:2016-02-25 00:25:05
【问题描述】:

给定以下代码:

interface Provider
{
    String getStuff();
}

class CustomProvider implements Provider
{
    public String getStuff() throws RuntimeException
    {
        // possibly throwing an exception

        return "some stuff";
    }
}

class Main
{
    public static void main(String[] args)
    {
        Main main = new Main();
        main.test(new CustomProvider());
    }

    public void test(Provider provider)
    {
        provider.getStuff(); // we don't know that this can throw an exception!
    }
}

在这种特殊情况下,我们知道这一点,但在另一种情况下可能不知道。

我们如何防止接口的实现抛出未经检查的异常但客户端方法不知道接口的具体实现的情况?

似乎有时对于未经检查的异常,您实际上永远无法知道调用方法是否会引发异常。

解决方案可以是更改接口中方法的签名:

interface Provider
{
    String getStuff() throws Exception;
}

这将确保该方法的客户端将被告知在所有实现中都可以抛出异常。这样做的问题是,可能没有一个接口的实现实际上会抛出异常。还在接口的每个方法中放置“抛出异常”看起来有点奇怪。

【问题讨论】:

  • 好问题,但基于您陈述的确切原因,您的观点相当公平。
  • 运行时异常就是这样:它们没有被检查。这就是为什么有文档的原因——这是传达 RTE 可能性的唯一方式。不仅可以抛出实际的RTE,还可以抛出它的子类。
  • 我觉得把throws AnyUncheckedException放在任何地方有点奇怪。
  • @DaveNewton 让我问你一个问题。如果一个方法接收到一个实现接口的对象,你是否认为“非常短视、非常危险和不负责任”? (记住:程序是接口,而不是实现)。您是否真的认为在不知道具体实现是什么的情况下使用接口“零意义”(毕竟,这就是接口的用途)。现在,如果您只知道您的参数实现了该接口,您将在哪里阅读文档?在界面?在所有的实现中?
  • @DaveNewton 为什么在使用接口时会关心实现?这不就是接口的用途,从实现中抽象出来吗?如果在一种情况下您需要了解有关实现的详细信息,那么您应该使用具体类而不是接口。但是在我在这里描述的情况下,假设您收到一个实现接口的对象作为参数(为了不依赖于特定的实现,例如抽象)。您不知道在参数中收到的具体类是什么。你是做什么的?

标签: java exception interface implementation


【解决方案1】:

似乎有时对于未经检查的异常,您实际上永远无法知道调用方法是否会引发异常。

编译器不检查未经检查的异常。您可以记录接口的约束,但编译器不会自动验证它们。

您建议声明该方法抛出异常。一个更受约束的方法是声明该方法抛出一些检查异常。

例如,您可以明确声明您的方法可能会引发 I/O 异常,如下所示。然后调用者只需要处理或抛出 IOException,而不是更多可能的异常。

interface Provider
{
    String getStuff() throws IOException;
}

另一种选择是声明一个特别的异常类型。这允许接口向调用者通告异常契约,而不限制实现可以遇到的异常类型。

interface Provider
{
    String getStuff() throws ProviderException;
}

class MyProvider implements Provider {
    public String getStuff() throws ProviderException {
       try {
          ...
       } catch ( IOException e ) {
          throw new ProviderException( e );
       }
    }
}

public class ProviderException extends Exception {
    public ProviderException( Exception cause ) {
        super( cause );
    }
    ...
}

【讨论】:

  • 我在接口中声明异常的建议是唯一可以警告客户端可以抛出异常的建议。但我不赞成那个“解决方案”(实际上对我来说更像是一个黑客)。首先,因为声明异常会添加实现细节,这是接口不应该做的事情。其次,要声明什么异常?使用“例外”变得广泛而模糊。我不同意使用“IOException”,因为可能该操作与输入输出操作无​​关。
  • @Mauricio 您必须在环境对您的限制范围内进行操作。您已经知道这些选项。如果没有“正确”的答案,并且您已经了解了解决方案,则不清楚您在寻找什么。
  • @DaveNewton 我知道环境的限制。我只是问是否有人可以提供更好的方法来解决这个问题。我说过,在我看来,没有“正确”的方法来做到这一点,而不是事实。我只是在寻找另一种方法来解决这种情况,就是这样。
  • @Mauricio - IOException 是一个更精确异常的示例。关键是大多数 throws 子句可以不那么模棱两可。 接口中的throws 子句不是实现细节——它是接口公共契约的一部分。 编译器使用该公共契约来验证调用者是否正确使用了该方法。您询问客户端方法如何知道在没有声明任何异常时可能会抛出哪些异常。检查或不检查。第一个通知调用者,后者不通知。
  • @AndyThomas 一个接口应该只声明什么是参数(如果有的话)和什么是返回值(如果有的话)。声明可以抛出什么异常是进入方法是如何实现的,而不是方法应该做什么。在设计接口时,如何事先知道具体类是否会抛出异常?假设今天您定义了一个不抛出任何异常的接口,并且它的所有实现也不抛出任何异常。万事皆安。如果明天其中一个实现必须抛出一个检查异常怎么办?
猜你喜欢
  • 2013-03-14
  • 2021-02-22
  • 1970-01-01
  • 2011-04-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多