When should you prefer Type::prop or ::ClassName over an equivalent lambda, and what are the trade-offs and pitfalls?
answer
- 1:1 delegation -> reference; extra logic -> lambda
- Need named/default args -> lambda, not ::Ctor
- Overloads need an explicit typed target
- References carry .name metadata
- Perf is the same; pick for clarity
basics
~20 sUse references when the lambda would just call one property or constructor (Person::name instead of { it.name }). They are shorter and clearer. Use a lambda when you need extra logic, defaults, or to control which overload is called.
solid answer
~40 sPrefer a **property/constructor reference** when a lambda would do nothing but a single property read or a plain constructor call: `Person::name` over `{ it.name }`, `ids.map(::User)` over `{ User(it) }`. Benefits: less noise, intent-revealing, reusable as a stored function value, and it carries reflection metadata (`.name`). Reach for a **lambda** when you need additional logic, argument reordering, default arguments, named arguments, to pick a specific **overloaded** constructor without a typed context, or to capture variables. Pitfalls: ambiguity when constructors/properties are overloaded (needs an explicit target type), the `kotlin-reflect` dependency for advanced reflection, and over-cleverness — a reference is only clearer when it maps 1:1 to the operation. Performance is comparable; both compile to function objects.
go deeper
Recognizes Person::name is shorter than { it.name }.
Knows when to convert lambdas to references and the readability benefit.
Articulates trade-offs: defaults/overloads/capture push toward lambdas; references give metadata and reuse.
Weighs API ergonomics, reflect dependency, generic inference, and team readability conventions.
## The decision rule Use a **callable reference** (`Type::prop`, `::ClassName`, `::function`) when the lambda's entire body is a single, direct delegation: - `{ it.name }` → `Person::name` - `{ User(it) }` → `::User` - `{ s -> s.trim() }` → `String::trim` (member) or `::trim` Use a **lambda** when any of these apply: - You add logic: `{ User(it).also(::audit) }`. - You reorder/select arguments: `{ a, b -> combine(b, a) }`. - You rely on **default or named arguments** of a constructor: `{ User(id = it, active = true) }` — a bare `::User` always supplies every parameter positionally. - You must choose a specific **overload** and the context type can't disambiguate. - You capture local state. ## Why references are nice ```kotlin data class Person(val name: String, val age: Int) val people: List<Person> = load() // References: minimal, declarative val names = people.map(Person::name) val byAge = people.sortedBy(Person::age) val byName = people.associateBy(Person::name) ``` - **Readability**: `Person::age` states "the age property" with no binding noise. - **Reusability**: store it — `val keyOf: (Person) -> String = Person::name`. - **Metadata**: `Person::name.name == "name"` powers generic serialization, validation, query builders. ## Trade-offs and pitfalls - **Overload ambiguity**: with several constructors of matching arity, `::Point` needs an explicit function type (`val f: (Int) -> Point = ::Point`). - **No defaults/named args** through a bare constructor reference — fall back to a lambda. - **`kotlin-reflect`**: heavy reflection (e.g. iterating `.parameters`, `callBy`) needs the `kotlin-reflect` artifact; simple use as a function value and member `.get`/`.set`/`.name` do not. - **Generics**: `::ClassName` for a generic type may need help: `val make: (T) -> Box<T> = ::Box`. - **Clarity over brevity**: don't force a reference where the operation isn't a clean 1:1 — a tiny lambda can read better. ## Performance References and lambdas both become function objects; inline higher-order functions (most of `kotlin.collections`) inline the call either way, so there's no meaningful runtime difference. Choose on **readability and capability**, not speed.
- Why might ::User not let you set a default-valued parameter?A bare constructor reference supplies all parameters positionally as a function; it cannot apply default or named arguments, so you use a lambda { User(id = it) } instead.
- Is there a performance reason to prefer a lambda over a reference?No. Both become function objects, and inline higher-order functions inline either. Choose based on readability and capability.
A reference is a pre-printed shortcut button; a lambda is a sticky note where you can scribble extra steps.
saying these in an interview costs you the question
- Claiming references are always faster than lambdas
- Forcing references where logic/defaults are needed, breaking named args
- Not recognizing overload ambiguity requires an explicit target type
- Believing every lambda should be converted to a reference