You want a suspend function and a SAM interface implementation to declare a checked exception to Java callers. Where exactly do you place @Throws, and what are the gotchas?
answer
- Targets: functions, constructors, accessors — not lambdas
- Accessors via @get:Throws / @set:Throws
- Suspend: legal, clause on continuation method
- Overrides do NOT inherit @Throws — repeat it
- For SAM: use named fun or object, not a lambda
basics
~20 sPut @Throws directly on the function or property accessor whose generated method should carry the throws clause. For suspend functions you can annotate them, but the clause lands on the suspending bytecode method; lambdas can't be annotated, so use an explicit function or object.
solid answer
~50 s`@Throws` targets functions, constructors, and property accessors — it edits the `throws` clause of whatever JVM method that declaration compiles to. For a **suspend** function, `@Throws` is allowed and adds the clause to the generated method (the one taking a `Continuation`), which matters for Java interop via Kotlin's `kotlinx-coroutines-jdk` bridges or callers that invoke the raw suspend method. For **property accessors**, you annotate the getter/setter via `@get:Throws` / `@set:Throws`. You **cannot** put `@Throws` on a **lambda** literal — there is no place for an annotation — so if Java must see a checked exception from a functional value, implement the SAM interface with a named function, an anonymous `object`, or annotate the interface method itself. Also remember the clause is per-method: an overridden function does not inherit the annotation; you must repeat `@Throws` on the override if Java callers of the subtype need it.
code
kotlin · 14 linesimport java.io.IOException
// property accessor
val data: String
@get:Throws(IOException::class)
get() = java.io.File("x").readText()
// suspend
@Throws(IOException::class)
suspend fun fetch(): String = TODO()
// override must repeat it
open class Base { @Throws(IOException::class) open fun read() {} }
class Impl : Base() { @Throws(IOException::class) override fun read() {} }go deeper
Knows @Throws goes on functions and is for Java interop, even if unsure about accessors/suspend.
Can place it on property accessors via @get:Throws and knows lambdas have no annotation site.
Handles suspend, SAM/lambda workarounds, and knows overrides do not inherit the annotation.
Reasons about how these placement rules interact with a stable Java-facing API and override hierarchies across module boundaries.
## Where `@Throws` is legal `@Throws` can be applied to: - **Functions** (top-level, member, extension). - **Constructors**. - **Property accessors** via annotation-use-site targets: `@get:Throws(...)` and `@set:Throws(...)`. Putting it on the property itself uses the default target, which is typically the accessor, but being explicit (`@get:`/`@set:`) avoids ambiguity. It **cannot** be applied to a **lambda expression** — a lambda has no declaration site to hang the annotation on. ## Suspend functions A `suspend` function compiles to a JVM method that takes an extra `Continuation` parameter and returns `Any?`. You **can** annotate a suspend function with `@Throws`, and the clause is emitted on that generated method: ```kotlin import java.io.IOException @Throws(IOException::class) suspend fun fetch(url: String): String { /* ... */ return "" } ``` This is mostly relevant when Java interoperates with coroutines through bridges (e.g. `kotlinx-coroutines-jdk8`/`future` adapters) or directly invokes the raw continuation-based method. For pure-Kotlin coroutine code the annotation is, as always, inert. ## SAM / functional interfaces and Java If Java needs to see a checked exception flowing out of a functional value, you cannot annotate a Kotlin **lambda**. Options: ```kotlin // 1) Named function reference @Throws(IOException::class) fun handler(input: String): String = TODO() // 2) Anonymous object implementing the interface val h = object : MyJavaInterface { @Throws(IOException::class) override fun run(x: String): String = TODO() } ``` If you control the interface, declare the checked exception on the **interface method** (in Java) or annotate the Kotlin override. ## Overrides do NOT inherit it Annotations like `@Throws` are **not inherited** by overrides. If a base declaration has `@Throws(IOException::class)` and a subclass overrides it, the override's generated method will **not** carry the clause unless you repeat the annotation. Java callers working against the subtype's static type may then lose the declared exception. ```kotlin open class Base { @Throws(IOException::class) open fun read(): String = "" } class Impl : Base() { // Must repeat @Throws if Java callers of Impl need the clause: @Throws(IOException::class) override fun read(): String = "" } ``` ## Gotchas summary - **Lambdas**: no annotation site — use a named function / `object`. - **Property accessors**: use `@get:Throws` / `@set:Throws`. - **Suspend**: legal; clause lands on the continuation-based method. - **Overrides**: not inherited — repeat the annotation. - It is still purely a signature concern; runtime is unaffected.
- Why can't you annotate a lambda with @Throws?A lambda is an expression, not a declaration, so there is no element to attach the annotation to; you must use a named function or an anonymous object implementing the interface.
- Does an override automatically get its base's @Throws clause?No. @Throws is not inherited; you must repeat it on the override or Java callers of the subtype lose the declared exception.
saying these in an interview costs you the question
- Believing @Throws can be placed on a lambda literal
- Assuming overrides inherit the base's @Throws clause
- Annotating a property instead of using @get:/@set: targets
- Thinking suspend functions cannot be annotated
- Forgetting it remains signature-only with no runtime effect