When and how do you pass explicit type arguments to a generic function, e.g. maxOf<Int>(...)? Why might you do it even when inference would work?
answer
- Syntax: name<Type>(args) — after the name, before ()
- All-or-nothing for multiple params (no partial)
- Must use when T is return-only with no target
- Optional use: widen type, disambiguate, document
- Idiomatic Kotlin prefers inference; <...> is the exception
basics
~10 sYou write the type in angle brackets right after the function name, like maxOf<Int>(a, b). You do it when Kotlin can't figure the type out, or to force a specific type or improve clarity.
solid answer
~40 sExplicit type arguments are written in angle brackets immediately after the function name and before the parentheses: maxOf<Int>(1, 2) or emptyList<String>(). You must supply them when inference is unconstrained — typically when the type parameter appears only in the return type with no expected target. You may also choose to supply them to override what inference would pick: maxOf<Any>(1, 2L) widens the result type, or to disambiguate overloads. They also document intent and prevent surprising least-upper-bound results. The arguments must satisfy any declared bounds. For a function with several parameters you provide them in order: pair<String, Int>("a", 1) — and it's all-or-nothing, you can't specify just one. Overuse is a smell: idiomatic Kotlin leans on inference, reserving explicit arguments for genuine ambiguity, return-only parameters, or deliberate widening.
code
kotlin · 7 linesval a = maxOf<Int>(1, 2) // explicit, though inference would also give Int
val b = maxOf<Long>(1, 2) // deliberate widening: result is Long
val e = emptyList<String>() // required: return-only parameter
fun <K, V> entry(k: K, v: V) = k to v
val p = entry<String, Int>("a", 1) // both type args, in order
// val q = entry<String>("a", 1) // ERROR: must give all or nonego deeper
Knows the name<Type>(args) syntax and that it's used when Kotlin asks for the type.
Explains both the required case (return-only parameter) and optional cases (widening, disambiguation), and the all-or-nothing rule.
Judges when explicit arguments aid readability vs add noise, and uses them to break inference failures or shape public API result types.
Sets team conventions on when explicit type arguments are warranted and designs signatures to minimize the need for them at call sites.
## Syntax Explicit **type arguments** go in angle brackets **after the function name** and before the call parentheses: ```kotlin maxOf<Int>(1, 2) emptyList<String>() listOf<Number>(1, 2L, 3.0) ``` For multiple type parameters, list them in declaration order; it is **all-or-nothing** — you cannot specify one and let the other be inferred: ```kotlin fun <K, V> entry(k: K, v: V): Pair<K, V> = k to v entry<String, Int>("a", 1) // both provided // entry<String>("a", 1) // ERROR: wrong number of type arguments ``` ## When you MUST provide them Inference has nothing to work from: ```kotlin fun <T> empty(): List<T> = emptyList() val x = empty<String>() // required: T only in return type, no target ``` ## When you CHOOSE to provide them (even though inference works) 1. **Widen the inferred type.** Inference picks the most specific type; you might want a broader one: ```kotlin val m = maxOf<Number>(1, 2L) // result typed Number instead of a narrower LUB val xs = listOf<Number>(1, 2) // List<Number>, not List<Int> ``` 2. **Disambiguate overloads** when several candidates match. 3. **Documentation / safety.** An explicit argument makes intent obvious and guards against an accidental least-upper-bound result (e.g. mixing types yielding `Any`). 4. **Break inference failures** in complex generic chains. ## Bounds still apply Explicit type arguments must satisfy the function's declared constraints; the compiler rejects arguments that violate an upper bound. ## Style Idiomatic Kotlin **prefers inference** — explicit type arguments are noise when the type is obvious. Reserve them for: return-only parameters, deliberate widening, disambiguation, or breaking an inference error. Reaching for `<...>` everywhere usually signals a misunderstanding of inference. ```kotlin // Idiomatic: let inference work val a = maxOf(1, 2) // Justified: deliberate widening val b = maxOf<Long>(1, 2) ```
- Can you specify only some of a function's type arguments?No. Type arguments are all-or-nothing per call: you either provide every type parameter or let the compiler infer them all.
- Give a reason to write explicit type arguments even when inference succeeds.To widen the result type (e.g. listOf<Number>(1, 2) instead of List<Int>), to disambiguate overloads, or to document intent and avoid an accidental Any from least-upper-bound.
saying these in an interview costs you the question
- Putting the type args before the name (Kotlin uses name<T>(...))
- Believing you can specify just one of several type parameters
- Using explicit type arguments everywhere instead of relying on inference
- Thinking explicit type arguments can bypass declared upper bounds
- Not knowing widening (listOf<Number>) as a legitimate use