【问题标题】:Throwing an Exception Not Defined in the Interface抛出接口中未定义的异常
【发布时间】:2010-11-16 10:16:45
【问题描述】:

当您需要抛出未在您正在实现的接口中定义的异常时,最佳做法是什么?

这是一个例子:

public interface Reader
{
    public abstract void read() throws IOException;
}

public class CarrotReader implements Reader
{
    public void read() throws IOException {}
}

public class CupcakeReader implements Reader
{
    public void read() throws IOException, CupcakeException {}
}

在这种情况下,您在读取纸杯蛋糕时发生了特定异常,因此您想抛出与此相关的异常。但是,Reader并没有在它的接口中定义这种类型的异常,那你怎么办呢?此外,在 Reader 接口的 throws 子句中添加 CupcakeException 是没有意义的,因为这种类型的异常是特定于 CupcakeReader。解决这个问题的一种方法是让 Reader 定义 read 以便它抛出一些父类型,例如 Exception,但随后您会丢失例外。在这种情况下你应该怎么做?谢谢!


另一个有趣的情况涉及一个您无法控制的界面。在这种情况下,表明出现问题的最佳方式是什么?

为了便于说明,这里是另一个例子:

public interface Reader
{
    public abstract void read();
}

public class CupcakeReader implements Reader
{
    public void read() throws CupcakeException {}
}

在这种情况下,您无法更改 Reader,但您想表明 CupcakeReaderread 方法出现问题。

【问题讨论】:

  • 这比我想象的还要糟糕。这样你甚至不能使用链接来规避系统。我认为你必须求助于(哎哟!)RuntimeExceptions
  • 我使用了 System.err.println 和 exception.printStackTrace();。这是合适的,还是应该像你说的那样用 RuntimeExecption 传播错误?
  • 我认为 log+swallow 在 99.99% 的情况下都不合适。由于这里提出的问题,这是 Java 开发人员中常见的(反?)模式。在我的“思想流派”中,只有三种有效的处理异常的方法:重新抛出、处理(即修复问题)或让它冒泡给可以处理它的人。永远不要吞咽。万一出了差错,后果自负。
  • 免责声明:这是我个人的观点。我认识一些人认为吞咽是可以接受的(我一直不明白为什么)。

标签: java exception interface throw


【解决方案1】:

您可能不得不创建一个预期类型的​​异常。

... catch(CupcakeException e) {
   throw new IOException("The sky is falling", e);
 }

【讨论】:

  • 这就是我现在正在做的事情。
  • 如果不能更改界面,这可能是最好的解决方案。您仍然可以访问您可能需要的所有信息,并且仍然遵守界面的约定。
  • @Scott,然后调用代码可以捕获 IOException,如果存在原因,则使用 THAT。如果不更改接口,您无法通过接口对更多已检查异常施加压力。接口是合约
【解决方案2】:

使用称为 ReaderException 的东西作为异常层次结构的根接口。 ReaderException 还将提供指向由于较低级别异常而引发的其他异常的链接。

【讨论】:

  • 这和在 throws 子句中添加 Exception 不是一回事吗?我意识到通过定义你自己的更具体的父类,你可以保留更多的上下文,但在某种程度上你仍然会丢失信息,对吧?
  • 这就是我通常所做的 (+1)。但是如果你不能改变界面(代码不是你的,代码已经发布等等)你会怎么做?
  • @Scott:你不会失去任何东西。如果您抛出一个扩展 ReaderExceptionCupcakeException,您可以将其捕获为 ReaderExceptionCupcakeException,具体取决于您想要的具体程度。
  • 因此,在您的 catch 块中,您必须捕获父类型(例如异常),然后使用 instanceof 进行检查以找出它的真正含义,对吗?这似乎有点乱。我也想知道您关于无法控制界面的问题的答案。
  • 这与捕获异常不同。事实上,我会将捕获异常称为一种罪过,因为您最终会无意中捕获其他运行时异常。例如,如果发生了,catch 块也会收到 NPE。调用代码不会对捕获和处理这些感兴趣。因此,使用您自己的异常层次结构是正确的方法。客户端可以选择捕获正确的异常并进行适当的处​​理(如果他愿意的话)
【解决方案3】:

异常是接口的一部分。如果可以重新定义接口,请为接口中的所有异常定义一个通用父级。

您还可以将 CupcakeException 设为 IOException 的子级。

【讨论】:

  • 对“也”投“赞成票”。但我有偏见,因为那是我最有可能做的(如果需要,声明异常的子类)。当然,拥有(例如)IOException 的子类假定调用者需要真正关心发生了更具体的事情,并暗示问题确实是某种与 I/O 相关的问题。
【解决方案4】:

只是不要使用已检查的异常。

您展示的示例是检查异常不好的原因之一。

但主要原因是您的纸杯蛋糕阅读器的用户必须处理您的异常,无论他是否对此感兴趣。

所以而不是:

Value value = reader.read();

你强迫他这样做:

Value value = null;
try {
    value = reader.read();
} catch (Exception e) {
    // now what??
}

value.doSomething();   // potential NPE here

想想哪个更好、更易读、更不容易出错,然后停止使用受检异常。

编辑:

我对负面评价感到惊讶。是否还有人认为受检异常很棒?如果是这样,这里有一些参考资料为什么你不应该使用检查异常:

  • 没有现代框架使用检查异常(Spring、EJB3 等)
  • 带有代码示例的文章here
  • 堆栈溢出topic
  • 有效的 Java(第 58 和 59 节)-here

【讨论】:

  • 我认为您发布了一个故意误导的示例。我会编写上面的代码来声明和使用 try{} 块中的变量。没有理由你必须编写像上面这样的代码(不管是否检查异常)
  • 检查异常的问题不是这里提出的问题。正如布赖恩指出的那样,这段代码是错误的。已检查异常的一个问题是,在调用引发异常的代码之前,您将失去执行先发制人检查(如果可以)的任何好处。即使您进行了先发制人的检查,您也不得不使用 try-catch 块。另一个事实是,如果你有 N 个方法调用 deeps 抛出的东西,并且只有调用 0 可以处理它,那么从 0 到 N 的每个调用都必须在合约中包含 throws(或者用RuntimeExceptions
  • 阅读本文以了解有关检查异常的“为什么不”的更多信息:artima.com/intv/handcuffs.html
  • 我不相信您可以实际进行先发制人的检查。谁能说在您进行检查和执行相关代码之间的条件保持不变。我意识到这两者会在时间上非常紧密地同时发生,但这仍然不能保证。
  • @Brian:当然可以。我即将加载一个配置文件,所以我检查它是否存在。如果是,我继续加载,不需要 try/catch。如果某个 ID10T 系统管理员坐在那里从我下面删除我的文件,这是一个正当理由在尖叫堆中炸毁一个未经检查的异常 - 因为它永远不会在 practice 中发生,如果确实如此,我们有比我丢失的配置文件更大的问题(即随意删除东西且未经授权的系统管理员)。
【解决方案5】:

也许你可以创建一个抽象的 ReaderSpecificException 类,把它放在接口中,然后 从这个抽象类继承 CupcakeException。

【讨论】:

  • 我认为将异常创建为抽象类不是一个好习惯。默认情况下,其中没有任何属性的异常仍然可以提供对问题的洞察(只需选择正确的名称)。为什么要将其定义为抽象类?
【解决方案6】:

如果您创建一个更高的抽象异常作为 CupCakeException 的基类,则不会像将 CupCakeException 添加到 Reader 接口时那样将 Reader 接口绑定到特定实现。 如果你不让一个异常从另一个继承,那么异常类中有一个constructor,它将一个可抛出的作为第二个参数,就像 Thorbjørn Ravn Andersen 在他的简短代码示例中已经展示的那样。这使您能够生成更抽象的异常,并且代码中需要了解更多信息的每个部分都可以查找更高异常的原因。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-14
    • 1970-01-01
    • 2020-04-29
    • 2016-03-26
    • 2021-03-11
    • 2011-05-30
    • 2011-02-24
    相关资源
    最近更新 更多