【问题标题】:Why can Java generics cast unassingable types (Every Exception to RuntimeExecption)?为什么 Java 泛型可以强制转换类型(RuntimeExecption 的每个异常)?
【发布时间】:2015-06-17 09:12:53
【问题描述】:

我想知道为什么下面的代码有效:

public static void throwUnchecked(Exception e) {
    TestGenericExceptionCast.<RuntimeException> doThrowUnchecked(e);
}

@SuppressWarnings("unchecked")
private static <T extends Exception> void doThrowUnchecked(Exception toThrow) throws T {
    throw (T) toThrow;
}

public static void main(String[] args) {
    throwUnchecked(new Exception());
}

为什么可以通过泛型将异常转换为 RuntimeException?

知道了:

根本不会发生强制转换,因为从来没有对象被分配给 T 类型的东西。

编译器被欺骗了,再也看不到它是一个检查异常。

【问题讨论】:

标签: java exception generics


【解决方案1】:

throw (T) toThrow; 并没有真正将toThrow 转换为RuntimeException。擦除后,类型绑定&lt;T extends Exception&gt; 意味着发生的实际转换是throw (Exception) toThrow;,它工作得很好。

但是,如果您将签名更改为:

private static <T extends RuntimeException> void doThrowUnchecked(Exception toThrow) throws T

它仍然会通过编译(因为RuntimeExceptionException 的子类型),但throwUnchecked(new Exception()); 会抛出ClassCastException,因为异常的运行时类型 - java.lang.Exception - 不能转换为java.lang.RuntimeException

如果这些方法的目的是将已检查的异常转换为未检查的异常,则可以删除这些方法并替换

throwUnchecked(new Exception());    

throw new RuntimeException (new Exception());

这会将已检查的异常包装在未检查的异常中。

【讨论】:

  • 正如我上面提到的,包装异常有几个不同之处。在大多数情况下,这可以正常工作,但我的操作是在不包装异常的情况下取消选中异常的解决方案。但我仍然不明白背后的机制:(
  • @BlackEye 您不能将已检查的异常类型转换为未检查的异常类型(就像您不能将 Animal 引用转换为 Dog 如果它实际上是 Cat 实例一样)。包装异常是我能想到的唯一方法。
  • @BlackEye 工作正常是什么意思?它通过编译,但不会将传递的异常转换为 RuntimeException。如果您更改代码使其实际转换为 RuntimeException,您将在运行时获得 ClassCastException。
  • "它通过了编译,但它不会将传递的异常转换为 RuntimeException。"这就是它的好处。
  • @BlackEye 所以基本上,你遇到了所有这些麻烦只是为了欺骗编译器让你抛出一个检查异常而不在 throws 子句中声明它。首先抛出 RuntimeExceptions 不是更简单吗?未处理的检查异常必须出现在 throws 子句中是有充分理由的。不过这是一个不错的技巧。
【解决方案2】:

因为RuntimeException extends Exception,这意味着Exception toThrow可以转换为T(即RuntimeException)。

附带说明,您可以通过以下方式避免@SuppressWarnings

@SuppressWarnings("unchecked")
private static <T extends Exception> void doThrowUnchecked(T toThrow) throws T {
    throw toThrow;
}

但是,有了这个,您必须将 ExceptionthrowUnchecked() 方法转换为 RuntimeException 或将其调整为 Generic,如下所示:

public static <T extends Exception> void throwUnchecked(T e) throws T {
    TestGenericExceptionCast.doThrowUnchecked(e);
}

最后,在main() 方法中,您可以这样称呼它:

public static void main(String[] args) {
    throwUnchecked(new RuntimeException());
}

此外,您可以通过将抛出的异常包装到 RuntimeException 来改进这一点:

public static <T extends Exception> void throwUnchecked(T toThrow) {
    throw new RuntimeException(toThrow);
}

public static void main(String[] args) throws Exception {
    throwUnchecked(new Exception());
}

【讨论】:

  • 您提到的代码在 throwUnchecked 方法中给出了编译错误。我不明白为什么“RuntimeException extends Exception”关系应该说明有关转换的任何内容。如果您尝试强制转换 RuntimeException 而不是 T,您会得到预期的 ClassCastException。
  • 您检查过我对throwUnchecked() 方法的更新了吗? :)
  • 不,对不起。 throwUnchecked() 方法的更新在哪里?
  • 此代码在使用非 RuntimeException 异常(例如 throwUnchecked(new Exception());)调用时仍会抛出 ClassCastException。
  • 但是现在这两种方法没有任何用处。您可以将throwUnchecked(new RuntimeException()); 替换为throw new RuntimeException(),它不会改变行为。
猜你喜欢
  • 2011-12-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-04
  • 1970-01-01
  • 2021-09-11
  • 1970-01-01
相关资源
最近更新 更多