使用Unsafe.getUnsafe().throwException抛出异常

8

我在java.lang.Class#newInstance0中发现了一些有趣的代码:

// Run constructor
try {
    return tmpConstructor.newInstance((Object[])null);
} catch (InvocationTargetException e) {
    Unsafe.getUnsafe().throwException(e.getTargetException());
    // Not reached
    return null;
}

请注意Unsafe.getUnsafe().throwException语句。它似乎是从一个没有声明会抛出已检查异常的方法中抛出异常!

为什么要这样做?
如果Sun开发人员可以使用此技术,那么我们也可以使用吗?

5个回答

4

使用Java和泛型,您可以利用类型擦除,并通过以下简单的技巧抛出任何异常:

public class Test {
    public static void main(String[] args) {
        doThrow(new SQLException());
    }

    public static void doThrow(Exception e) {
        Test.<RuntimeException> doThrow0(e);
    }

    @SuppressWarnings("unchecked")
    public static <E extends Exception> void doThrow0(Exception e) throws E {
        throw (E) e;
    }
}

有点恐怖,但是……


3

他们为什么要这样做?

我不完全确定,但我认为他们肯定有很好的理由。(当你深入研究反射调用时,涉及一些非常棘手的事情...)

如果Sun开发人员可以使用这种技术,那么我们也可以使用吗?

我会说不行...除非你有一个非常非常好的理由。未声明抛出已检查异常的方法违反了最小惊奇原则。想想那个可能必须在关键生产系统中调试你代码的可怜家伙,而且还是半夜3点。

仅仅因为一些Sun工程师认为在特定情况下某些东西是个好主意,并不一定意味着它在普遍情况下是个好主意。


2

Don Schwarz写了一篇有趣的博客文章,讨论了如何避免使用被检查异常(Checked Exceptions),文章链接:Avoiding Checked Exeptions。感谢分享。


很遗憾,链接已经失效了。 - undefined

1
使用Unsafe类的问题在于它仅供内部使用,没有定义的接口,并且在不同平台上是不同的。您可以使用标准调用来实现相同的功能(虽然已弃用)。
Thread.currentThread().stop(checkedException);

不安全的代码现在基本上已经成为 API 的一部分了。 - Enerccio

1
显然,Sun这样做是为了允许从Class.newInstance()直接抛出已检查的异常,而不是将其包装在InvocationTargetException中或在签名上声明throws Exception。这是一个“有趣”的设计决策,因为它与其他地方的模式不同--例如,Constructor.newInstance()将包装任何构造函数异常在InvocationTargetException中。它所提供的“好处”是:1.能够直接捕获构造函数异常,以其原始类型;2.避免newInstance()需要声明throws Exception和调用者捕获Exception。缺点是:3.未声明已检查的异常。这消除了已检查异常的主要优势/目的,并可能产生警告,因为catch块由于未声明已检查的异常而无法到达。这会成为你在代码中使用的一种模式吗?对我来说,这似乎相当不可能。它似乎主要适用于通用/框架代码,其中由框架包装的客户端代码可能引发已检查的异常。然后,只有在希望在不声明可以抛出它们的情况下传播已检查的异常时,才有必要使用它。对我来说,这是值得怀疑的。
大多数包装客户端代码的Java框架方法都是按照以下三种异常范例之一设计的:
1. 纯运行时异常,或 2. 声明throws Exception以允许完整的已检查异常,或 3. “在包装器中重新抛出”方法(考虑使用未经检查的异常作为包装器)。
这个特定的Class.newInstance方法非常不寻常,通过不声明可能抛出的已检查异常,它绕过/取消了已检查异常唯一的真正好处。
通常,已经发生了从已检查异常向运行时异常的重大转变,我不建议使用Unsafe.throwException()(也不建议Lukas上面提到的“泛型hack”)。
对于应用程序代码,如果可能的话,请使用运行时异常并声明所抛出的异常类型。对于框架代码,请考虑上述三种范例,优先考虑运行时异常。
背景:http://literatejava.com/exceptions/checked-exceptions-javas-biggest-mistake/

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接