Can `invoke` be overloaded and can it be a suspend function? Show how multiple `invoke` overloads resolve and how a suspending callable object behaves.
answer
- invoke uses normal overload resolution
- suspend operator fun invoke is legal
- UseCase pattern: useCase(params)
- SuspendFunctionN shape for suspend invoke
- watch for ambiguity with defaults/vararg
basics
~10 sYes. A type can declare several invoke overloads with different parameters, and the compiler picks the matching one. invoke can also be marked suspend, so calling obj(...) works inside a coroutine and can suspend.
solid answer
~40 s`invoke` follows ordinary overload-resolution rules: you can declare `operator fun invoke()`, `operator fun invoke(x: Int)`, `operator fun invoke(x: String)`, etc., and `obj(arg)` resolves by argument types like any overloaded method — with the usual specificity/ambiguity rules. You can also write `suspend operator fun invoke(...)`, producing a callable object whose call point may suspend; this is common in clean-architecture 'UseCase' objects (`suspend operator fun invoke(params): Result`) invoked as `useCase(params)` inside a coroutine. Such a type corresponds to a `SuspendFunctionN` shape. Caveats: overloads must differ enough to avoid ambiguity; default arguments can collide with other overloads; and mixing suspend and non-suspend `invoke` overloads is allowed but the suspend one is only callable from a coroutine context. `vararg`, generics, and reified-free type parameters all work on `invoke` as on normal functions.
code
kotlin · 10 linesclass GetOrders(private val repo: OrderRepo) {
suspend operator fun invoke(userId: Long): List<Order> =
repo.loadFor(userId)
}
// usage inside a coroutine
suspend fun show(getOrders: GetOrders, uid: Long) {
val orders = getOrders(uid) // suspends; reads like a function call
println(orders.size)
}go deeper
May know overloading exists but not that invoke supports suspend.
Writes overloads and a basic suspend invoke, but may miss resolution subtleties.
Explains overload resolution, suspend context rules, and the UseCase pattern crisply.
Connects to architecture (DI, testability) and reasons about API ambiguity and coroutine context propagation.
## Overloading `invoke` `invoke` is resolved like any other member function, so a single type can expose several overloads: ```kotlin class Router { operator fun invoke() = "home" operator fun invoke(path: String) = "page:$path" operator fun invoke(id: Int) = "item:$id" } val r = Router() r() // "home" r("about") // "page:about" r(7) // "item:7" ``` The compiler uses standard overload resolution: pick the most specific applicable signature; report an error if the call is ambiguous. Default parameter values and `vararg` interact with this exactly as they do for normal functions, so two overloads that both match an empty or single-arg call can cause ambiguity. ## suspend `invoke` You can mark the operator `suspend`: ```kotlin class FetchUser(private val api: Api) { suspend operator fun invoke(id: Long): User = api.getUser(id) } // in a coroutine: val user = fetchUser(42L) // suspends here ``` This is the idiomatic 'UseCase/Interactor' pattern: the object is a single-responsibility callable, and `useCase(params)` reads like a function call while remaining injectable, testable, and stateful. A `suspend operator fun invoke` corresponds to the `SuspendFunctionN` interface family, so such objects line up with `suspend` function types. ## Mixing and resolution notes - You may have both `operator fun invoke(...)` and `suspend operator fun invoke(...)` overloads; the suspend one is only callable from a coroutine/`suspend` context. - Generic `invoke` (`operator fun <T> invoke(x: T)`) is allowed; type inference applies. - Ambiguity errors arise the same way as for normal overloaded methods — design signatures to be distinguishable. ## Why senior-relevant The pattern shows up in DI-driven architectures and DSLs. Knowing that `invoke` is just an operator-named method — subject to normal overloading, suspension, generics — lets you reason about resolution and coroutine context correctly rather than treating callable objects as magic.
- Where can you call a `suspend operator fun invoke`?Only from a coroutine or another `suspend` function — the same restriction as any suspend function; `obj(...)` does not bypass coroutine context rules.
- What can cause an overloaded `invoke` call to fail to compile?Ambiguity: if two overloads are equally applicable for the given arguments (often due to defaults or implicit conversions), the compiler reports an ambiguous-call error.
saying these in an interview costs you the question
- Claiming `invoke` cannot be overloaded
- Saying suspend `invoke` can be called outside a coroutine
- Treating overload resolution for `invoke` as special/different
- Thinking the UseCase pattern needs reflection
- Ignoring ambiguity risks from default args