在 Java 中扩展 Throwable

2022-09-01 10:29:49

Java 允许您创建一个全新的子类型,例如:Throwable

public class FlyingPig extends Throwable { ... }

现在,很少,我可能会做这样的事情:

throw new FlyingPig("Oink!");

当然还有其他地方:

try { ... } catch (FlyingPig porky) { ... }

我的问题是:

  • 这是个坏主意吗?如果是这样,为什么?
    • 如果这是一个坏主意,可以做些什么来防止这种子类型化?
    • 既然这是不可预防的(据我所知),可能导致什么灾难?
  • 如果这不是一个坏主意,为什么不呢?
    • 你怎么能从你能做到的事实中做出一些有用的东西?extends Throwable

建议方案 #1

我真的想做这样的事情的场景具有以下属性:

  • “事件”是最终会发生的事情。这是意料之中的。它绝对不是一个,当它发生时,也没有什么-al。ErrorException
    • 因为它是预期的,所以会有一个等待它。它不会“滑过”任何东西。它不会“逃避”任何一般和/或.catchcatchExceptionError
  • “事件”很少发生。
  • 当它发生时,通常会有一个很深的堆栈跟踪。

因此,也许现在很清楚我想说的是:是详尽的递归搜索的结果FlyingPig

要搜索的对象是存在的:这只是在搜索空间的大海中找到它的问题。搜索过程将是一个漫长的过程,因此异常处理相对昂贵的成本可以忽略不计。事实上,使用标志的传统控制流构造替代方案可能更昂贵,因为它必须在整个搜索过程中不断检查,最有可能是在递归的每个级别。此检查将在 99.99% 的时间内失败,但绝对有必要传播终止条件。在某种程度上,虽然有效但检查效率低下boolean isFound

通过在找到所搜索的对象时简单地 -ing a,您不必将代码与标志的管理混为一谈。代码不仅在这方面更干净,而且由于这种遗漏,它可能会运行得更快。throwFlyingPigboolean isFound

因此,总而言之,选择是在以下两者之间:

  • 传统的控制流方法
    • 使用 ,已连续检查boolean isFound
    • 99.99%的时间,支票是一种“浪费”,因为它仍然是false
    • 当它最终变成 时,你停止递归,你必须确保你可以正确地放松到最初的调用。true
  • FlyingPig方法
    • 不要打扰任何.boolean isFound
    • 如果找到,只需 ;这是意料之中的,所以会有一个。throw new FlyingPig()catch
    • 没有标志的管理,如果你需要继续前进,没有浪费的支票,没有簿记来手动解除递归,等等。boolean

问题:

  • 这种 (ab) 使用异常的技术是否有效?(它有名字吗?
  • 如果有效,应该,或者是否就好了?(即使它的情况没有什么特别之处?FlyingPig extends ThrowableException

答案 1

我会说这是一个非常糟糕的主意。许多代码的实现都基于这样的假设:如果你捕获并且你已经捕获了所有可能的异常。大多数教程和教科书都会告诉你同样的事情。通过创建直接子类,您可能会创建各种维护和互操作性问题。ErrorExceptionThrowable

我想不出有什么好的理由来扩展。扩展或代替。ThrowableExceptionRuntimeException

编辑 - 回应OP提出的方案#1。

异常是处理“正常”流量控制的一种非常昂贵的方式。在某些情况下,我们正在谈论执行数千条额外的指令来创建,抛出和捕获异常。如果要忽略公认的智慧并对非异常流控制使用异常,请使用子类型。试图通过声明是作为子类型来假装某事是“事件”而不是“例外”不会取得任何成就。ExceptionThrowable

但是,将异常与错误,错误,错误,等等混为一谈是错误的。而且,使用 的子类来表示“不是错误、错误、错误或其他任何情况的特殊事件”没有错。关键是事件应该是例外的;即,与众不同,很少发生,...Exception

总之,a 可能不是错误,但这并不是不将其声明为 的子类型的理由。FlyingPigException


答案 2

这种 (ab) 使用异常的技术是否有效?(它有名字吗?

在我看来,这不是一个好主意:

  • 它违反了最小惊讶原则,它使代码更难阅读,特别是如果它没有任何例外。
  • 抛出异常在Java中是一个非常昂贵的操作(虽然在这里可能不是问题)。

异常不应用于流控制。如果我必须命名这种技术,我会称之为代码气味或反模式。

另请参阅:

如果有效,应该扩展,还是刚刚好?(即使它的情况没有什么特别之处?FlyingPigThrowableException

在某些情况下,您可能不仅想捕捉而且很少见,人们通常不会捕捉。但是我没有找到一种情况,即你想抛出一个子类。ThrowableExceptionErrorThrowableThrowable

我还认为,扩展不会使事情看起来不那么“例外”,它们会让它看起来更糟 - 这完全违背了意图。Throwable

所以总而言之,如果你真的想扔一些东西,扔一个子类。Exception

另请参阅:


推荐