题
Java让您创建一个全新的子类型 Throwable
, ,例如:
public class FlyingPig extends Throwable { ... }
现在, 非常稀有, ,我可能会做这样的事情:
throw new FlyingPig("Oink!");
当然还有其他地方:
try { ... } catch (FlyingPig porky) { ... }
我的问题是:
- 这是一个坏主意吗?如果是这样,为什么?
- 如果这是一个坏主意,该怎么办?
- 既然它是不可预防的(据我所知),那么可能会导致什么灾难?
- 如果这不是一个坏主意,为什么不呢?
- 您如何从可以的事实中做出有用的东西
extends Throwable
?
- 您如何从可以的事实中做出有用的东西
建议的方案#1
我当时的场景 真的 试图做类似的事情具有以下属性:
- “事件”是 将要 最终发生。这是 预期的. 。绝对不是
Error
, ,什么都没有Exception
- 何时发生。- 因为它是 预期的, ,会有一个
catch
等待它。它不会“滑过”任何东西。它不会“逃脱”任何尝试catch
一般的Exception
和/或Error
.
- 因为它是 预期的, ,会有一个
- “事件”发生 极少.
- 发生这种情况时,通常会有深层堆栈痕迹。
所以也许现在很清楚我想说的是: FlyingPig
是个 结果 详尽的递归搜索。
存在要搜索的对象:这只是在大海中找到它的问题,即搜索空间。搜索过程将是一个漫长的过程,因此相对昂贵的异常处理成本可以忽略不计。实际上,传统的控制流构造替代了使用 boolean isFound
国旗可能更昂贵,因为必须在整个搜索过程中连续检查它,这很可能在递归的每个级别上。此检查的时间为99.99%,但绝对有必要传播终止条件。在某种程度上 有效的, ,检查是 效率低下!
简单地 throw
- a FlyingPig
当找到寻求的对象时,您不必用管理的管理 boolean isFound
旗帜。不仅在这方面的代码清洁器,而且由于此遗漏,它可能会更快地运行。
因此,总而言之,选择在这两个之间:
- 传统的控制流方法
- 用一个
boolean isFound
, ,连续检查 - 99.99%的时间,支票是“浪费”,因为它仍然是
false
- 当它最终变成
true
, ,您停止递归,必须确保可以正确放松初始电话。
- 用一个
FlyingPig
方法- 不要打扰任何
boolean isFound
. - 如果发现,只是
throw new FlyingPig()
;它是 预期的, ,所以会有一个catch
为了它。 - 没有管理
boolean
旗帜,不浪费检查您是否需要继续前进,没有簿记以手动放松递归,等等。
- 不要打扰任何
问题:
- (AB)使用异常的技术有效吗? (有一个名字吗?)
- 如果有效,应该
FlyingPig extends Throwable
, ,或者是Exception
正好? (即使其情况没有任何异常?)
解决方案
我会说这是一个非常糟糕的主意。实施了很多代码,假设如果您抓住 Error
和 Exception
您遇到了所有可能的例外。大多数教程和教科书都会告诉您同样的事情。通过创建一个直接的子类 Throwable
您可能正在创建各种维护和互操作性问题。
我想不到没有充分的理由扩展 Throwable
. 。延长 Exception
或者 RuntimeException
反而。
编辑 - 响应OP的建议方案#1。
例外是一种处理“正常”流控制的非常昂贵的方法。在某些情况下,我们正在谈论 成千上万 执行的额外说明以创建,投掷和捕获例外。如果您要忽略公认的智慧,并使用异常进行非效率流控制,请使用 Exception
亚型。试图假装某件事是“事件”而不是“例外”,这是一个亚型 Throwable
不会取得任何成就。
但是,将异常与错误,错误,错误等混合在一起是错误的。还有 没有什么不对 代表“使用错误,错误,错误或任何其他子类的“异常事件” Exception
. 。关键是该事件应该是 例外; IE是出于普通的,很少发生,...
总而言之 FlyingPig
可能不是错误,但这没有理由不将其声明为子类型 Exception
.
其他提示
(AB)使用异常的技术有效吗? (有一个名字吗?)
我认为,这不是一个好主意:
- 它违反了 最少惊讶的原则, ,它使代码更难读取,尤其是在没有例外的情况下。
- 投掷例外是Java中非常昂贵的操作(尽管在这里可能不是问题)。
例外应该只是 不用于流控制. 。如果我不得不命名这种技术,我将其称为代码气味或反图案。
也可以看看:
如果有效,应该
FlyingPig
扩展Throwable
, ,或者是Exception
正好? (即使其情况没有任何异常?)
您可能想在某些情况下 抓住 Throwable
不仅要抓住 Exception
但是也 Error
但这很少见,人们通常不会抓住 Throwable
. 。但是我未能找到您想要的情况 扔 一个子类 Throwable
.
我也认为延伸 Throwable
不会使事情看起来更少 “异常”, ,它们看起来更糟 - 完全打败了意图。
因此,总而言之,如果您真的想扔东西,请扔一个子类 Exception
.
也可以看看:
这是热点建筑师约翰·罗斯(John Rose)的博客文章:
http://blogs.oracle.com/jrose/entry/longjumps_considered_inexpenge
这是关于流量控制的“滥用”例外。略有不同的用例,但是..简而言之,它确实可以很好地工作 - 如果您进行预选/克隆您的异常,以防止创建堆栈跟踪。
我认为如果对客户“隐藏”,这种技术是合理的。即,您的飞行犬永远不应该离开您的图书馆(所有公共方法都应提供传统保证不要扔它)。保证这一点的一种方法是使其成为一个例外。
我认为扩展投掷的唯一理由是因为您想允许人们传递有捕获(例外)条款的回调,并希望您的结果被他们忽略。我几乎可以买...
这 org.junit.Test
注释包括 None
延伸的类 Throwable
并用作 expected
注释参数。
如果您可以证明是什么使FlyingPig与错误和异常相距甚远,以使其不适合作为两个子类,那么创建它没有根本上的错误。
我能想到的最大问题是在务实的世界中,有时有合理的理由可以抓住java.lang.exception。您的新型投掷类型将飞过trycatch块,这些块对抑制(或伐木,包装等)的所有合理期望都会飞行。
另一方面,如果您在旧系统上进行维护 联合国合理地抑制java.lang.exception,您可以围绕它作弊。 (假设对时间正确修复它的真诚呼吁被拒绝)。
随着这个问题的发展,我看到我误解了原始问题的重点,因此其他一些答案可能更相关。我将在此处留下这个答案,因为它仍然可能对有类似问题的其他人有帮助,并在寻找他们的问题的答案时找到此页面。
扩展可抛出(并处理)自定义异常没有错。但是,您应该牢记以下几点:
- 使用最具体的例外是一个好主意。它将允许呼叫者以不同的方式处理不同类型的异常。例外的类层次结构对牢记很重要,因此您的自定义异常应扩展另一种类型的可抛出式,该类型可尽可能接近您的异常。
- 扩展可抛出可能太高了。尝试扩展异常或Runtime Exception(或更接近您投掷异常的较低级别异常)。请记住,Runtime Exception和异常之间的区别。
- 呼叫抛出异常(或异常子类)的方法需要包裹在能够处理异常的尝试/捕获块中。这对于您期望出现问题的情况是有好处的,因为可能无法控制的情况(例如,网络下降)。
- 对抛出RuntimeException(或其子类)的方法的调用不需要包裹在可以处理异常的try/Catch块中。 (当然可能是,但不需要。)这更多的是不应该期望的例外。
因此,假设您的代码库中有以下异常类:
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) {
...
}