A Kotlin function throws a checked Java exception. Why might a Java caller fail to catch it, and how does @Throws fix this?
answer
- Default Kotlin bytecode has no throws clause
- Java error: 'exception is never thrown in try block'
- @Throws(IOException::class) re-adds the throws clause
- Interop-only: no effect on Kotlin callers or runtime
- Accepts multiple KClass args; works on suspend too
basics
~20 sBecause Kotlin doesn't record a throws clause in the compiled method, Java thinks the exception can't happen and refuses to let you catch it. Adding @Throws puts the clause back so Java can catch it.
solid answer
~40 sKotlin has no checked exceptions, so by default a compiled Kotlin function has **no `throws` clause** in its bytecode signature — even if it actually throws `IOException`. When Java calls that function and writes `catch (IOException e)`, the Java compiler sees no declared checked exception in the try block and reports *"exception IOException is never thrown in the corresponding try block."* The fix is the `@Throws(IOException::class)` annotation on the Kotlin function: it adds the checked exception to the generated method's `throws` clause, so the Java compiler permits (and where required, demands) the catch/declare. `@Throws` accepts multiple `KClass` arguments. It is interop-only — it has no effect on Kotlin callers and does not introduce checked-exception enforcement within Kotlin.
code
kotlin · 7 linesimport java.io.IOException
import java.sql.SQLException
@Throws(IOException::class, SQLException::class)
fun loadAndStore(path: String) {
// body may throw either checked exception; Java callers can now catch them
}go deeper
Recognizes that @Throws lets Java catch a checked exception thrown by Kotlin.
Explains that Kotlin omits the throws clause and that the Java compiler then rejects the catch.
Details the compile-time signature mechanics, multiple-type usage, and that it's interop-only with no Kotlin-side effect.
Designs cross-language API contracts: when to surface checked exceptions vs Result/sealed errors, and framework reflection dependencies on throws clauses.
## The root cause Java's compiler tracks **checked exceptions**: it forbids catching a checked type that the try block's methods don't declare via `throws`. Kotlin, having no checked exceptions, omits the `throws` clause from compiled method signatures by default. So a Kotlin function that genuinely throws `IOException` *looks* exception-free to Java. ```kotlin // Kotlin fun risky() { throw java.io.IOException("boom") } ``` ```java // Java caller — DOES NOT COMPILE try { KotlinFileKt.risky(); } catch (IOException e) { // error: IOException is never thrown in try block handle(e); } ``` ## The fix: @Throws Annotate the Kotlin declaration so the compiler emits the `throws` clause: ```kotlin @Throws(java.io.IOException::class) fun risky() { throw java.io.IOException("boom") } ``` Now the generated method is effectively `void risky() throws IOException`, and the Java `catch (IOException e)` compiles. You can list several types: `@Throws(IOException::class, SQLException::class)`. ## What @Throws does NOT do - It does **not** add checked-exception enforcement for **Kotlin** callers — they still need no try/catch. - It does **not** change runtime behavior — the exception was always thrown; this only affects the **compile-time signature** Java sees. - It is **not** required when Kotlin calls Kotlin, or even when Java calls a Kotlin function whose exception you don't intend to catch as a checked type. ## When you actually need it - Java callers must `catch`/`declare` a specific checked type from your Kotlin API. - Frameworks that **reflect** on the `throws` clause (some test runners, RMI, certain serialization/proxy libraries) expect it. - Coroutines: `@Throws` can also annotate `suspend` functions; the clause appears on the generated continuation-based signature for Java interop. ## Relationship to this leaf This is the mirror image of "calling Java that throws checked exceptions": that direction is frictionless (no try/catch forced); this direction needs `@Throws` to re-expose the contract Java relies on. ## Key keywords/APIs `@Throws(vararg exceptionClasses: KClass<out Throwable>)`, checked vs unchecked exceptions, the JVM method `throws` clause, `suspend` interop.
- Does @Throws make Kotlin callers wrap the call in try/catch?No. Kotlin still has no checked exceptions; @Throws only affects how Java sees the method signature.
- Can you use @Throws on a suspend function?Yes. It declares the checked exception on the generated Java-facing signature, which matters for Java interop with coroutine-based code.
@Throws is like adding an allergen label back onto a product so a strict inspector (the Java compiler) will let it through customs — the contents never changed.
saying these in an interview costs you the question
- Saying @Throws enables checked-exception enforcement inside Kotlin
- Thinking the runtime behavior changes (it doesn't — only the signature)
- Believing Java can always catch any Kotlin-thrown exception by default
- Claiming @Throws is needed when Kotlin calls Kotlin