When can the return type of a single-expression function be inferred, and when must you still declare it explicitly?
answer
- Inference works for expression bodies only
- Recursion REQUIRES explicit type
- Declare to widen to an interface/supertype
- explicitApi() forces public return types
- Block bodies never infer (except Unit)
basics
~20 sThe compiler can usually figure out the return type from the expression, so you can leave it off. But for public API functions, recursive functions, or when you want a different type than what is inferred, you should write the type explicitly.
solid answer
~40 sWith an expression body the compiler infers the return type from the right-hand side, so `fun sq(x: Int) = x * x` is `Int`. You MUST declare it explicitly when: (1) the function is recursive — the inferrer cannot resolve a type that depends on itself; (2) you want to widen/narrow the type, e.g. return a supertype or interface rather than the concrete inferred class; (3) the body's inferred type is `Nothing` or a platform type and you need a definite one. It is also strongly recommended (and enforceable via the `explicitApi()` compiler mode) to declare return types on every public/published API function so the type is a deliberate contract, not an accident of implementation. Block-body functions returning non-Unit always require an explicit return type — inference is exclusive to expression bodies.
code
kotlin · 5 lines// recursion needs an explicit type
fun factorial(n: Int): Long = if (n <= 1) 1L else n * factorial(n - 1)
// declare to expose a wider contract than the inferred MutableList
fun roles(): Set<String> = mutableSetOf("admin", "user")go deeper
Knows the type can often be left off and the compiler fills it in.
Names the recursion exception and the widen-the-contract case; knows block bodies don't infer.
Brings up explicitApi() and treats public return types as deliberate contracts during reviews.
Sets org-wide policy (explicit-api strict for libraries) balancing brevity in internal code against contract stability.
## Inference: how it works For an expression-body function, the compiler computes the static type of the expression after `=` and uses it as the return type: ```kotlin fun square(x: Int) = x * x // inferred: Int fun name() = "KataJob" // inferred: String fun first(xs: List<T>) = xs[0] // inferred: T ``` ## Cases where you MUST declare the type ### 1. Recursion The inferrer needs the function's own type to type-check the body, which is circular, so it fails: ```kotlin // Does NOT compile — recursive expression body without a declared type fun fib(n: Int) = if (n < 2) n else fib(n - 1) + fib(n - 2) // Fix: declare the return type fun fib(n: Int): Int = if (n < 2) n else fib(n - 1) + fib(n - 2) ``` ### 2. You want a different (usually wider) type Inference picks the *most specific* type. To expose an interface/supertype as the contract: ```kotlin fun ids() = mutableListOf(1, 2, 3) // inferred MutableList<Int> fun ids(): List<Int> = mutableListOf(1, 2, 3) // contract is read-only List ``` ### 3. Avoiding accidental Nothing / platform types If an expression yields `Nothing` (e.g. ends in `throw` or `TODO()`) or a Java-derived *platform type* (`String!`), declaring the type pins down a definite, non-surprising contract. ## Public API: explicitApi() Kotlin's `explicitApi()` / `-Xexplicit-api=strict` compiler mode **requires** explicit return types and visibility on every public declaration. Libraries enable it so a refactor of an implementation can't silently change the published return type. Even without it, the convention is: declare return types on public functions. ## Block bodies A block-body function returning anything other than `Unit` **always** needs an explicit return type — inference applies only to expression bodies. (Returning `Unit` may be omitted in both forms.) ## Summary rule Infer for small private helpers; declare explicitly for recursion, widened contracts, and all public API.
- Why does a recursive expression-body function fail to compile without a declared type?Type inference would need the function's own return type to type-check its body, which is circular, so the compiler reports it cannot infer the type and asks you to specify it.
- What does explicitApi() change about single-expression functions?It makes the compiler error on any public/protected declaration that omits an explicit return type or visibility, turning inferred types into deliberate, stable API contracts.
saying these in an interview costs you the question
- Believing recursive expression-body functions can infer their type
- Saying block-body functions also infer non-Unit return types
- Never declaring return types on public library APIs
- Not realizing inference picks the most specific type (e.g. MutableList over List)
- Confusing variable type inference rules with function return inference