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 正好? (即使其情况没有任何异常?)
有帮助吗?

解决方案

我会说这是一个非常糟糕的主意。实施了很多代码,假设如果您抓住 ErrorException 您遇到了所有可能的例外。大多数教程和教科书都会告诉您同样的事情。通过创建一个直接的子类 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, ,因为两者兼而有之 FlyingPigSwine 延长 Pig. 。如果您想做同样的事情来处理任何一个异常,这将很有用。但是,您可以以不同的方式处理它们:

try {
    bar();
} catch (FlyingPig e) {
    ...
} catch (Swine e) {
    ...
}

玩!框架 使用这样的东西进行请求处理。请求处理经过许多层(路由,中间件,控制器,模板渲染),在渲染的HTML的最后一层中 包裹 并投掷,最高层捕获,拆开并发送给客户端。因此,所涉及的许多层中的方法都不需要明确返回响应对象,也不需要将响应对象作为参数传播和修改。

我对详细信息有点粗略。您可以浏览游戏代码!详细信息的框架。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top