1. 概述
在本篇文章中,我们将探讨 Kotlin 中为何没有“受检异常(checked exceptions)”,以及这种设计带来的影响。在此基础上,我们会深入讲解 Kotlin 提供的 @Throws
注解,以及它在 Java 互操作性中的关键作用。我们还会讨论在哪些场景下应该使用 @Throws
,哪些场景下则应该避免使用。
2. Kotlin 中没有受检异常
和 Java 不同,Kotlin 不支持受检异常机制。这意味着在 Kotlin 中,我们不需要像 Java 那样在方法签名中使用 throws
声明异常。事实上,Kotlin 甚至根本就没有 throws
关键字。
来看一个例子:
fun throwJavaUnchecked() {
throw IllegalArgumentException()
}
fun throwJavaChecked() {
throw IOException()
}
上面的两个函数分别抛出了 Java 中的非受检异常(IllegalArgumentException
)和受检异常(IOException
),但在 Kotlin 中它们的处理方式是一样的 —— 都不需要在方法签名中声明异常。
✅ 结论:在 Kotlin 中,无论抛出的是受检还是非受检异常,都不需要显式声明。
3. Java 互操作性问题
Kotlin 与 Java 的互操作性非常好,这是其一大优势。但正因为 Kotlin 不支持受检异常,当我们在 Java 中调用 Kotlin 函数时,就会遇到一些问题。
例如,我们从 Java 调用 throwJavaUnchecked()
并捕获 IllegalArgumentException
是没问题的:
public class Caller {
public static void main(String[] args) {
unchecked();
}
public static void unchecked() {
try {
ThrowsKt.throwJavaUnchecked();
} catch (IllegalArgumentException e) {
System.out.println("Caught something!");
}
}
}
因为 IllegalArgumentException
是非受检异常,Java 编译器不会报错。
❌ 但如果我们尝试从 Java 调用 throwJavaChecked()
并捕获 IOException
:
public static void checked() {
try {
ThrowsKt.throwJavaChecked();
} catch (IOException e) {
System.out.println("Won't even compile");
}
}
编译器会报错:
java: exception java.io.IOException is never thrown in body of corresponding try statement
⚠️ 原因:Java 要求受检异常必须在方法签名中使用 throws
声明,否则不能捕获。
4. 使用 @Throws 注解
为了解决上述 Java 互操作性问题,Kotlin 提供了 @Throws
注解。
当我们使用 @Throws
注解一个 Kotlin 函数时,编译器会在生成的字节码中为该函数添加 throws
子句,这样 Java 调用方就可以正确地捕获受检异常。
示例:
@Throws(IOException::class)
fun throwJavaChecked() {
throw IOException()
}
✅ Java 调用方现在可以这样使用:
try {
ThrowsKt.throwJavaChecked();
} catch (IOException e) {
System.out.println("It works this time!");
}
或者选择继续抛出:
public static void checked() throws IOException {
ThrowsKt.throwJavaChecked();
}
⚠️ 注意:@Throws
只对 Java 调用者有意义,对 Kotlin 代码本身没有任何影响。
4.1. 注解的字节码表示
使用 @Throws
后,我们可以通过 javap
查看字节码确认其效果。
编译 Kotlin 文件:
kotlinc throws.kt
查看字节码:
javap -c -p com.baeldung.throwsannotation.ThrowsKt
输出如下:
public static final void throwJavaChecked() throws java.io.IOException;
可以看到,函数签名中确实包含了 throws java.io.IOException
。
而没有 @Throws
的函数则没有:
public static final void throwJavaUnchecked();
5. 总结
- ✅ Kotlin 不支持受检异常机制。
- ✅
@Throws
是 Kotlin 提供给 Java 调用者使用的注解,用于声明函数可能抛出的受检异常。 - ❌ 在纯 Kotlin 项目中使用
@Throws
是没有意义的。 - ✅ 使用
@Throws
后,Kotlin 编译器会在字节码中生成throws
子句,从而实现与 Java 的无缝互操作。
📌 最佳实践建议:
- 如果你的 Kotlin 函数会被 Java 代码调用,并且可能抛出受检异常,请务必加上
@Throws
。 - 如果是纯 Kotlin 项目,无需使用该注解,保持代码简洁。
如需查看完整示例代码,欢迎访问:GitHub 项目地址