skip to content

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?

level: middleimportance: should knowfreq 45%

answer

  1. operator fun invoke in companion object
  2. ClassName(...) -> Companion.invoke(...)
  3. private constructor + public invoke
  4. caching / polymorphic return / validation
  5. real constructor wins resolution

basics

~10 s

Put 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 s

A `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 lines
kotlin
interface 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

for a junior

Recognizes the syntax but may not know constructor-vs-invoke resolution.

for a middle

Builds the pattern with private constructor and explains caching/validation motives.

for a senior

Weighs companion invoke vs top-level factory and discusses discoverability/return polymorphism.

for a principal

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

context