How do you create a class whose only way to be constructed is through a named factory function on its companion object, hiding the real constructor?
answer
- private constructor + companion object
- companion shares the class's private scope
- User.create instead of User(...)
- validate/normalize before constructing
- named, descriptive entry point
basics
~10 sMark the constructor private and add a function in the companion object that creates and returns the instance. Callers use the named function instead of calling the constructor directly.
solid answer
~40 sDeclare the primary constructor private with `class User private constructor(...)`. Add a `companion object` with a named function (e.g. `fun create(...)` or `of(...)`) that can call that private constructor because the companion is a member of the class and shares its private scope. Callers then write `User.create(...)` instead of `User(...)`. This gives you a named, descriptive entry point, lets you run validation or normalization before construction, allows returning cached or subtype instances, and prevents anyone from bypassing the factory. Companion members are accessed via the class name, so `User.create` reads naturally. You can have several differently named factories for different construction scenarios.
code
kotlin · 10 linesclass User private constructor(val name: String, val age: Int) {
companion object {
fun create(name: String, age: Int): User {
require(age >= 0) { "age must be >= 0" }
return User(name.trim(), age)
}
}
}
val u = User.create(" Ada ", 36) // User(...) is not callable from herego deeper
Knows the private constructor syntax and that a companion function returns the instance via User.create.
Explains why the companion can reach the private constructor (shared private scope) and names benefits like validation and naming.
Contrasts factory vs secondary constructor, discusses instance control (caching, subtypes), and when each is appropriate.
Frames it as an API-design boundary: stable named entry points decouple callers from construction details and enable future evolution without breaking source/binary compatibility.
## What this is A **factory function** is a function whose job is to build and return an object, instead of constructing it directly with the constructor. In Kotlin the idiomatic place for such a function is the class's **companion object** — a single object tied to the class itself (like Java `static` members) accessed through the class name. ## Private constructor The primary constructor is made private with the `private constructor` modifier: ```kotlin class User private constructor(val name: String, val age: Int) { companion object { fun create(name: String, age: Int): User { require(age >= 0) { "age must be non-negative" } return User(name.trim(), age) } } } ``` Because the **companion object is a member of the class**, it sits inside the class's private scope and is therefore allowed to call the private constructor. Code outside the class cannot write `User("a", 1)` — it must call `User.create(...)`. ## Why do this - **A descriptive name**: `User.create`, `Color.fromHex`, `Duration.ofSeconds` read better than an overloaded constructor. - **Validation / normalization** before the object exists (here `require` and `trim`). - **Control over instances**: you can cache, return a cached singleton, or return a subtype. - **Single entry point**: nobody can bypass the factory. ## How callers use it ```kotlin val u = User.create(" Ada ", 36) // name becomes "Ada" ``` There is no `User(...)` available to outside code. `User.create` works because companion members are resolved through the class name without an instance. ## Note The constructor is still *called* inside the factory — the factory does not replace construction, it wraps it behind a named, validated gateway.
- Can the companion call the private constructor even though it's private?Yes. The companion object is a member of the class, so it has access to the class's private members, including a private constructor.
- Why use a factory function instead of a secondary constructor?A factory can have a meaningful name, can return cached or subtype instances, and can fail by returning null or a Result; a constructor must always return a new instance of exactly that type and cannot be named.
A private constructor with a companion factory is like a building with no public door: you must go through the reception desk (the factory), which checks your ID before letting you in.
saying these in an interview costs you the question
- Thinking a private constructor blocks the companion too
- Calling it 'static' without understanding companion is a real object instance
- Believing the factory replaces the constructor rather than wrapping it
- Saying you must use reflection to call a private constructor from the companion