Java 使用接口隐藏实现引发的异常

Java 使用接口隐藏实现引发的异常,java,exception,interface,implementation,Java,Exception,Interface,Implementation,给定以下代码: 接口提供程序 { 字符串getStuff(); } 类CustomProvider实现提供程序 { 公共字符串getStuff()引发RuntimeException { //可能会引发异常 返回“一些东西”; } } 班长 { 公共静态void main(字符串[]args) { Main Main=新Main(); test(新的CustomProvider()); } 公共无效测试(提供程序) { provider.getStuff();//我们不知道这会引发异常! } }

给定以下代码:

接口提供程序
{
字符串getStuff();
}
类CustomProvider实现提供程序
{
公共字符串getStuff()引发RuntimeException
{
//可能会引发异常
返回“一些东西”;
}
}
班长
{
公共静态void main(字符串[]args)
{
Main Main=新Main();
test(新的CustomProvider());
}
公共无效测试(提供程序)
{
provider.getStuff();//我们不知道这会引发异常!
}
}
在这种特殊情况下,我们知道它,但在另一种情况下可能不知道

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

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

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

接口提供程序
{
字符串getStuff()引发异常;
}
这将确保该方法的客户端得到通知,在所有实现中都可以抛出异常。这样做的问题是,可能接口的所有实现都不会实际抛出异常。另外,在接口的每个方法中放置“抛出异常”看起来有点奇怪

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

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

建议声明该方法引发异常。一种更受约束的方法是声明该方法抛出一些已检查的异常

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

interface Provider
{
    String getStuff() throws IOException;
}
另一个选项是声明特殊异常类型。这允许接口向调用方公布异常契约,而不限制实现可能遇到的异常类型


这是一个好问题,但基于您陈述的确切原因,您的观点是相当正确的。这就是运行时异常的问题:它们没有被检查。这就是为什么会有文档——这是传达RTE可能性的唯一方式。不仅可以抛出一个实际的
RTE
,还可以抛出它的一个子类。我认为将
抛出任何未选中的异常
有点奇怪。@DaveNewton让我问你一个问题。如果一个方法接收到一个实现接口的对象,你会看到“明显的短视、彻底的危险和不负责任”吗?(请记住:程序是一个接口,而不是一个实现)。你真的认为在不知道具体实现是什么的情况下使用接口“毫无意义”吗(毕竟,接口就是为了这个目的)。现在,如果您只知道您的参数实现了该接口,那么您将在哪里阅读文档?在界面中?在所有的实现中?@DaveNewton为什么在使用接口时会关心实现?从实现中抽象出接口不是为了这个吗?如果在某种情况下需要了解有关实现的详细信息,那么应该使用具体类而不是接口。但是在我在这里描述的情况下,假设您收到一个实现接口的对象作为参数(为了不依赖于特定的实现,例如,抽象)。您不知道在参数中接收到的具体类是什么。你会怎么做?我建议在接口中声明异常,这是唯一一个可以警告客户端可能引发异常的方法。但我不赞成这种“解决方案”(实际上对我来说更像是一种黑客)。首先,因为在那里声明异常会添加实现细节,这是接口不应该做的事情。第二,要声明什么例外情况?使用“例外”变得更加广泛和模糊。我不同意使用“IOException”,因为该操作可能与输入输出操作无关。@Mauricio您必须在环境对您的限制范围内操作。你已经知道了选择。如果没有“正确”的答案,并且您已经理解了解决方案,那么您就不清楚您在寻找什么。@DaveNewton我知道环境的限制。我只是想问一下,万一有人能提供更好的方法来处理这个问题。我说在我看来没有“正确”的方法来做这件事,这不是事实。我只是在寻找另一种方法来解决这种情况,就是它。@Mauricio-IOException是一个更精确的异常的例子。关键是大多数抛出的子句可以不那么模棱两可。接口中的
抛出
子句不是实现细节——它是接口公共契约的一部分。编译器使用该公共约定来验证调用方是否正确使用了该方法。您会问,当没有声明异常时,客户机方法如何知道可能会抛出哪些异常。检查,或不检查。第一个通知调用者,后者不通知。@andythonas接口应该只声明什么是参数(如果有的话)和什么是返回值(如果有的话)。声明可以抛出哪些异常是为了了解该方法的实现方式,而不是该方法应该做什么。在设计接口时,如何事先知道具体类是否会抛出异常?假设今天您定义了一个不会抛出任何异常的接口,并且它的所有实现都不会抛出任何异常
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 );
    }
    ...
}