How can you define `invoke` on a companion object to create a factory that looks like a constructor call, and why would you do that?
answer
- operator fun invoke in companion object
- ClassName(...) -> Companion.invoke(...)
- private constructor + public invoke
- caching / polymorphic return / validation
- real constructor wins resolution
basics
~10 sPut operator fun invoke(...) inside a class's companion object. Then ClassName(...) actually calls that factory method, letting you add logic (validation, caching, choosing a subtype) while still looking like a normal constructor.
solid answer
~40 sA `companion object` is a singleton tied to its class, accessed via the class name. If you declare `operator fun invoke(...)` on it, then `ClassName(args)` resolves to `ClassName.Companion.invoke(args)` — a fake constructor. This is useful when you want construction-like syntax but need behavior a constructor can't give: returning a cached instance, returning a different subtype/implementation, performing validation that throws a domain exception, or hiding the real constructor (`private constructor`). Kotlin's own stdlib does this — for example list/sequence-style factory functions. The trade-off: it can confuse readers who assume `ClassName(...)` is a true constructor, and IDEs may navigate to the companion. Prefer it when the construction needs polymorphism or caching; prefer a plain top-level factory function or named secondary constructor otherwise. Overload resolution will prefer a real constructor over a companion `invoke` when signatures collide.
code
kotlin · 14 linesinterface Shape
private class Circle(val r: Double) : Shape
private class Square(val s: Double) : Shape
class Shapes {
companion object {
operator fun invoke(kind: String, size: Double): Shape = when (kind) {
"circle" -> Circle(size)
else -> Square(size)
}
}
}
val s: Shape = Shapes("circle", 2.0) // polymorphic 'constructor'go deeper
Recognizes the syntax but may not know constructor-vs-invoke resolution.
Builds the pattern with private constructor and explains caching/validation motives.
Weighs companion invoke vs top-level factory and discusses discoverability/return polymorphism.
Frames it as an API design decision; considers binary compatibility and team conventions.
## The pattern A `companion object` is a singleton declared inside a class; its members are reachable through the enclosing class name. Because the class name then refers to an object, you can give that object an `invoke` operator and get constructor-looking calls: ```kotlin class Color private constructor(val rgb: Int) { companion object { private val cache = HashMap<Int, Color>() operator fun invoke(rgb: Int): Color = cache.getOrPut(rgb) { Color(rgb) } } } val red = Color(0xFF0000) // looks like a constructor, actually Color.Companion.invoke(...) ``` Here the real constructor is `private`, so all creation funnels through the companion's `invoke`, which caches instances (a flyweight). Callers still write `Color(0xFF0000)`. ## Why use it instead of a constructor - **Caching / interning**: return an existing instance instead of always allocating. - **Polymorphic return**: choose and return a subtype/implementation based on the arguments — constructors must return their exact type. - **Validation with domain errors** before the object exists. - **Hiding the constructor**: combine `private constructor` with a public `invoke`. ## Resolution detail When both a real constructor and a companion `invoke` could match a call, Kotlin prefers the **constructor**. So companion `invoke` does not override a public constructor of the same signature — it supplements (e.g., when the constructor is private or has a different signature). ## Caveats - Readers may assume `ClassName(...)` is a true constructor; the companion factory is less discoverable. - A simpler alternative is a **top-level factory function** named like the class (`fun Color(rgb: Int): Color`), which the stdlib uses heavily (e.g., `listOf`-style or capitalized factory functions) and which is often clearer than `invoke`. Use companion `invoke` when you specifically want the *call-the-type* ergonomics together with hidden construction logic.
- If a class has both a public constructor and a companion `invoke` with the same signature, which one runs for `ClassName(args)`?The real constructor wins; companion `invoke` only takes over when the constructor is unavailable (e.g., private) or has a different signature.
- When would a top-level factory function be better than companion `invoke`?When you don't need the type-as-callable ergonomics — a named or capitalized top-level function is more discoverable and conventional in Kotlin.
It's like a concierge desk in front of a building: you still 'enter the building' (call the type), but the concierge can hand you an existing room key (cached instance) or send you to a different floor (subtype).
saying these in an interview costs you the question
- Saying companion `invoke` overrides a public same-signature constructor
- Claiming constructors can return a subtype (they cannot)
- Forgetting `private constructor` is what hides real construction
- Confusing companion `invoke` with `init` blocks
- Thinking companion `invoke` requires the `operator` keyword to be optional