Java允许您创建一个全新的Throwable
子类型,例如:
public class FlyingPig extends Throwable { ... }
现在,很少,我可能会这样做:
throw new FlyingPig("Oink!");
当然其他地方:
try { ... } catch (FlyingPig porky) { ... }
我的问题是:
extends Throwable
?我真的想要做类似这样的事情的场景具有以下属性:
Error
,并且没有Exception
- 关于它何时发生。
catch
等待它。它不会“滑倒”过去。它不会“逃避”任何catch
一般Exception
和/或Error
的尝试。所以也许我现在很清楚我要说的是:FlyingPig
是详尽的递归搜索的结果。
要搜索的对象存在:只有在大海中找到它才是搜索空间。搜索过程很长,因此相对昂贵的异常处理成本可以忽略不计。实际上,使用boolean isFound
标志的传统控制流构造替代方案可能更昂贵,因为必须在整个搜索过程中连续检查,最有可能在递归的每个级别。此检查将在99.99%的时间内失败,但绝对有必要传播终止条件。在某种程度上,当有效时,检查效率低下!
当找到所寻找的对象时,只需throw
- FlyingPig
,您就不必使用boolean isFound
标志的管理来混乱代码。在这方面,代码不仅更清晰,而且由于这种遗漏,它可能会运行得更快。
总而言之,选择是在这两者之间:
boolean isFound
,连续检查false
true
时,您将停止递归,并且您必须确保可以正确地展开初始调用。FlyingPig
接近
boolean isFound
。throw new FlyingPig()
;它是预期,因此它会有catch
。boolean
标志,没有浪费检查是否需要继续进行,没有记账手动解除递归等等。问题:
FlyingPig extends Throwable
或Exception
应该没问题? (即使它的情况没有什么特别之处?)答案 0 :(得分:32)
我会说这是一个非常糟糕的主意。许多代码都是在假设你抓住Error
和Exception
的情况下实现的,你已经捕获了所有可能的异常。大多数教程和教科书都会告诉你同样的事情。通过创建Throwable
的直接子类,您可能会创建各种维护和互操作性问题。
我认为没有充分理由延长Throwable
。改为Exception
或RuntimeException
。
编辑 - 响应OP提议的方案#1。
异常是处理“正常”流量控制的一种非常昂贵的方式。在某些情况下,我们正在讨论数千个执行的额外指令,以创建,抛出和捕获异常。如果您要忽略已接受的智慧并使用异常进行非异常流控制,请使用Exception
子类型。试图假装某事是一个“事件”,而不是“例外”,宣称它是Throwable
的子类型无法实现任何目标。
然而,将异常与错误,错误,错误等混淆是错误的。并且没有错误使用Exception
的子类表示“非错误,错误,错误或其他”的异常事件。关键是该事件应该例外;也就是说,与众不同,很少发生......,
总之,FlyingPig
可能不是错误,但没有理由不将其声明为Exception
的子类型。
答案 1 :(得分:8)
这种(ab)使用异常的技术有效吗? (有名字吗?)
在我看来,这不是一个好主意:
例外应该是not be used for flow control。如果我不得不说出这种技术,我会把它称为代码气味或反模式。
另见:
如果有效,
FlyingPig
是Throwable
延长,还是Exception
是否合适? (即使它的情况没有什么特别之处?)
在某些情况下,您可能希望捕捉 Throwable
,不仅要抓住Exception
,还要 Error
,但这种情况很少见人们通常不会抓住Throwable
。但是我找不到你想要抛出 Throwable
的子类的情况。
我还认为延长Throwable
并不会使事情看起来更少“exception-al”,它们会让事情变得更糟 - 这完全打败了意图。
总而言之,如果你真的想扔东西,抛出一个Exception
的子类。
另见:
答案 2 :(得分:7)
以下是来自HotSpot架构师John Rose的博客文章:
http://blogs.oracle.com/jrose/entry/longjumps_considered_inexpensive
关于流量控制的“滥用”异常。用例略有不同,但.. 简而言之,它运行得非常好 - 如果您预先分配/克隆您的异常以防止创建堆栈跟踪。
我认为如果客户“隐藏”这种技术是合理的。 IE,你的FlyingPig永远不能离开你的图书馆(所有公共方法都应该保证不丢弃它)。保证这一点的一种方法是使其成为一个检查过的例外。
我认为扩展Throwable的唯一理由是因为你想允许人们传递具有catch(异常e)子句的回调,并且希望你的结果被他们忽略。我可以买那个......
答案 3 :(得分:4)
org.junit.Test
注释包含扩展None
的{{1}}类,并用作Throwable
注释参数的默认值。
答案 4 :(得分:4)
如果你能证明FlyingPig与Error和Exception的区别,那么它就不适合作为它们的子类,那么创建它就没有根本的错误。
我能想到的最大问题是在务实的世界中,有时候有合理的理由来捕获java.lang.Exception。你的新类型的throwable将会飞过try-catch块,它们会尽可能地抑制(或记录,包装,等等)每个可能的非致命问题。
另一方面,如果你正在对一个 un 有理由压制java.lang.Exception的旧系统进行维护,你可能会欺骗它。 (假设真正呼吁时间真正正确地解决它被拒绝)。
答案 5 :(得分:2)
随着这个问题的发展,我明白了 我误解了这一点 原来的问题,所以有些 其他答案可能更多 相关。我会留下这个答案 在这里,因为它可能仍然有用 其他有类似问题的人, 并在搜索时找到此页面 回答他们的问题。
将Throwable扩展为能够抛出(和处理)自定义异常没有任何问题。但是,您应该牢记以下几点:
因此,假设您的代码库中有以下异常类:
public class Pig extends Throwable { ... }
public class FlyingPig extends Pig { ... }
public class Swine extends Pig { ... }
public class CustomRuntimeException extends RuntimeException { ... }
和一些方法
public void foo() throws Pig { ... }
public void bar() throws FlyingPig, Swine { ... }
// suppose this next method could throw a CustomRuntimeException, but it
// doesn't need to be declared, since CustomRuntimeException is a subclass
// of RuntimeException
public void baz() { ... }
现在,您可以使用一些代码来调用这些方法:
try {
foo();
} catch (Pig e) {
...
}
try {
bar();
} catch (Pig e) {
...
}
baz();
请注意,当我们致电bar()
时,我们可以抓住Pig
,因为FlyingPig
和Swine
都会延伸Pig
。如果你想做同样的事情来处理任何一个异常,这很有用。但是,您可以采用不同的方式处理它们:
try {
bar();
} catch (FlyingPig e) {
...
} catch (Swine e) {
...
}
答案 6 :(得分:2)
Play! framework使用类似的东西来处理请求。请求处理遍历多个层(路由,中间件,控制器,模板呈现),在最后一层,呈现的HTML为wrapped in a throwable并抛出,最顶层捕获,解包并发送到客户端。因此,涉及的许多层中的所有方法都不需要显式返回响应对象,也不需要将响应对象作为参数传递以进行传播和修改。
我对细节有点粗略。你可以浏览Play的代码!细节框架。