使用Throwable以外的事情除了例外

时间:2011-08-01 03:22:43

标签: java stack throw throwable

我总是在错误的背景下看到Throwable / Exception。但是我可以想到,扩展一个Throwable只是为了打破一堆递归方法调用会非常好。比如说,您试图通过递归搜索的方式在树中查找并返回一些对象。一旦你发现它将它粘贴在某些Carrier extends Throwable并抛出它,并在调用递归方法的方法中捕获它。

正面:您不必担心递归调用的返回逻辑;既然你找到了你需要的东西,为什么还要担心如何将这个引用备份到方法堆栈中。

否定:您有一个不需要的堆栈跟踪。 try/catch块也变得违反直觉。

这是一个愚蠢的简单用法:

public class ThrowableDriver {
    public static void main(String[] args) {
        ThrowableTester tt = new ThrowableTester();
        try {
            tt.rec();
        } catch (TestThrowable e) {
            System.out.print("All good\n");
        }
    }
}

public class TestThrowable extends Throwable {

}

public class ThrowableTester {
    int i=0;

    void rec() throws TestThrowable {
        if(i == 10) throw new TestThrowable();
        i++;
        rec();
    }
}

问题是,是否有更好的方法来达到同样的目的?另外,以这种方式做事有什么不妥之处吗?

6 个答案:

答案 0 :(得分:7)

答案 1 :(得分:3)

使用程序控制流程的异常不是一个好主意。

对于超出正常操作标准的情况,准确保留异常。

关于SO,有很多相关的问题:

答案 2 :(得分:2)

语法变得不稳定,因为它们不是为一般控制流而设计的。递归函数设计的标准做法是返回一个标记值或找到的值(或者没有任何东西,在你的例子中可以工作)。

传统智慧:“例外情况适用于特殊情况。”正如您所注意到的,Throwable听起来在理论上更为通用,但除了例外和错误之外,它似乎并未设计为更广泛的用途。来自docs

  

Throwable类是所有错误和异常的超类   用Java语言编写。

许多运行时(VM)的设计不是围绕抛出异常进行优化,这意味着它们可能“昂贵”。当然,这并不意味着你不能做到这一点,“昂贵”是主观的,但一般情况下这并没有完成,其他人会惊讶地发现你的代码。

答案 3 :(得分:1)

  

问题是,是否有更好的方法来达到同样的目的?也,   以这种方式做事有什么本质上的坏处吗?

关于第二个问题,无论编译器的效率如何,异常都会带来很大的运行时负担。仅此一点应该反对在一般情况下使用它们作为控制结构

此外,异常相当于受控制的getos,几乎相当于跳远。是的,是的,它们可以嵌套,而在像Java这样的语言中,你可以拥有漂亮的“终极”块。尽管如此,它们都是如此,因此,它们并不是典型控制结构的 通用案例 替代品。超过四十年的集体工业知识告诉我们,一般来说,我们应该避免这样的事情 除非 你有充分理由这样做。

这就是你的第一个问题的中心。是的,有一种更好的方法(以你的代码为例)......只需使用典型的控制结构:

// class and method names remain the same, though using 
// your typical logical control structures

public class ThrowableDriver {
    public static void main(String[] args) {
        ThrowableTester tt = new ThrowableTester();
        tt.rec();
        System.out.print("All good\n");
        }
    }
}

public class ThrowableTester {
    int i=0;

    void rec() {
        if(i == 10) return;
        i++;
        rec();
    }
}

请参阅?简单。减少代码行数。没有多余的try / catch或不必要的异常抛出。你实现了同样的目标。

最后,我们的工作不是使用语言结构,而是创建合理的程序,对于可维护性的观点来说足够简单,只需要足够的语句来完成工作而不需要其他任何工作。

因此,当谈到您提供的示例代码时,您必须问自己:使用典型控制结构时我无法获得该方法的哪些内容?

  

您不必担心递归调用的返回逻辑;

如果您不担心返回逻辑,则只需忽略返回或将您的方法定义为void类型。将它包装在try / catch中只会使代码变得比必要的复杂。如果你不关心回报,我相信你关心完成的方法。所以你只需要简单地调用它(就像我在这篇文章中提供的代码示例一样)。

  

既然你找到了你需要的东西,为什么还要担心如何将这个引用备份到方法堆栈中。

在方法完成之前将返回(在JVM中几乎是对象引用)推送到堆栈比执行抛出异常(运行epilogs并填充可能更大)的所有簿记更便宜堆栈跟踪)并捕获它(遍历堆栈跟踪。)JVM与否,这是基本的CS 101内容。

因此,不仅价格昂贵,您还需要输入更多字符来编写相同的内容。

实际上没有递归方法可以通过Throwable退出,不能使用典型的控制结构重写。你需要有一个非常非常好的理由来使用异常来代替控制结构。

答案 4 :(得分:0)

只需。不要。
请参阅:Effective Java by Joshua Bloch, p. 243

答案 5 :(得分:-1)

我不知道这是否是一个好主意,但是在设计 CLI(不使用准备好的库)时,我想到了一种处理从应用程序中的某个位置返回而不会弄乱系统的自然方法stack 是使用一个 Throwable(如果你只是调用你从中找到这个方法的方法,如果有人说在应用程序菜单中前进和后退大约 255 次,你将得到堆栈溢出)。由于返回使用 Throwable 与您在应用程序中的位置无关,因此它使我能够使方法抽象(字面意义),即,由类 X 的某些条目组成的所有菜单都用一种方法处理.