skip to content

When should you use KFunction.callBy() instead of call(), and how do you build its KParameter map to honor default arguments?

level: middleimportance: must knowfreq 45%

answer

  1. callBy(Map<KParameter, Any?>) — omit to use default
  2. Only omit when KParameter.isOptional == true
  3. Include INSTANCE receiver param for members
  4. Omit key != map-to-null (null is explicit)
  5. Frameworks use it on primaryConstructor

basics

~20 s

Use callBy() when the function has default parameters and you only want to supply some of them. You pass a Map<KParameter, Any?> with entries only for the parameters you provide; omitted ones with defaults are filled in automatically.

solid answer

~40 s

callBy(args: Map<KParameter, Any?>) invokes a callable by associating values with specific KParameter objects (from kFunction.parameters) rather than positionally. Its big advantage: any KParameter you LEAVE OUT of the map uses its default value, as long as parameter.isOptional is true. You must still include all non-optional parameters (including the INSTANCE receiver) or it throws IllegalArgumentException. This is exactly how Kotlin itself can construct objects with defaults reflectively — frameworks like kotlinx.serialization and Jackson's Kotlin module use callBy on the primary constructor. Note callBy is heavier than call(): it computes a default-mask bitmask and synthetic 'default' bridge. For nullable parameters you can pass null explicitly; that's different from omitting the key, which triggers the default.

code

kotlin · 10 lines
kotlin
import kotlin.reflect.full.primaryConstructor

data class Conn(val host: String, val port: Int = 5432, val ssl: Boolean = false)

fun main() {
    val ctor = Conn::class.primaryConstructor!!
    val p = ctor.parameters.associateBy { it.name }
    val c = ctor.callBy(mapOf(p["host"]!! to "db", p["ssl"]!! to true))
    println(c)   // Conn(host=db, port=5432, ssl=true)
}

go deeper

for a junior

Knows callBy exists and uses a parameter map instead of positions.

for a middle

Builds the map keyed by KParameter, leverages isOptional to skip defaults, and includes the receiver correctly.

for a senior

Explains the synthetic $default method + bitmask mechanism and the omit-vs-null distinction, and caches for performance.

for a principal

Designs deserialization/DI factories on callBy, reasons about its cost vs generated code, and handles edge cases (varargs, nullable defaults).

## call() vs callBy() - `call(vararg args: Any?)` — **positional**, must supply EVERY parameter, ignores defaults. - `callBy(args: Map<KParameter, Any?>)` — **by-parameter**, omitted optional parameters fall back to their **default values**. Use `callBy()` whenever defaults matter — typically constructing objects from partial data (deserialization, DI, test factories). ## Building the map Keys are the real `KParameter` instances from `function.parameters`. Look them up by name or kind: ```kotlin import kotlin.reflect.full.primaryConstructor data class User(val name: String, val active: Boolean = true, val age: Int = 0) val ctor = User::class.primaryConstructor!! val byName = ctor.parameters.associateBy { it.name } val u = ctor.callBy(mapOf(byName["name"]!! to "Ann")) // active=true, age=0 come from defaults ``` ## Key rules - A parameter is skippable only if **`KParameter.isOptional == true`** (it has a default). Omitting a non-optional key → `IllegalArgumentException`. - The **INSTANCE** receiver (for member functions) is itself a `KParameter` of kind `INSTANCE` and must be in the map for non-static members. - **Omitting a key** triggers the default; **mapping a key to `null`** passes `null` explicitly (only valid if the parameter is nullable, checked via `KParameter.type.isMarkedNullable`). - `isVararg` parameters: provide an array as the value. ## Why frameworks rely on it `callBy` is the only public reflective way to honor Kotlin default arguments. Under the hood the compiler emits a synthetic `<init>$default`/`fn$default` method plus an Int bitmask of which args are missing; `callBy` constructs that mask for you. It is measurably slower than `call()`, so hot paths often cache the constructor and the parameter map.

  • How do you skip a parameter so its default is used?
    Simply don't put that KParameter key in the map. The runtime sets the default-value bitmask bit for it. The parameter must have isOptional == true.
  • What is the difference between omitting a key and mapping it to null?
    Omitting uses the declared default value; mapping to null passes null as the actual argument (allowed only for nullable parameters).

callBy() is an online order form: fields you leave blank take their default value, but required fields must be filled or the form is rejected.

saying these in an interview costs you the question

  • Saying call() can honor default parameters
  • Omitting a non-optional parameter and expecting it to work
  • Confusing 'omit key' with 'map to null'
  • Forgetting the INSTANCE receiver key for member functions
  • Claiming callBy is as cheap as call()

context