Factory Methods

Private Constructor + Companion Factory

A companion object can call the private constructor. This lets you validate input, return cached instances, or return null instead of throwing - things a constructor cannot do.

Example

class Email private constructor(val value: String) {
    override fun toString() = value

    companion object {
        fun of(raw: String): Email? {
            val clean = raw.trim().lowercase()
            return if (Regex("^[a-z0-9._]+@[a-z0-9.]+\\.[a-z]{2,}$").matches(clean)) Email(clean) else null
        }
    }
}

fun main() {
    println(Email.of("  [email protected] "))
    println(Email.of("not-an-email"))
}

Output

[email protected]
null

Named Companion and Interfaces

Example

A companion object can have a name and implement interfaces.

interface JsonParser<T> { fun parse(json: String): T }

data class City(val name: String, val pin: String) {
    companion object Parser : JsonParser<City> {
        override fun parse(json: String): City {
            val parts = json.removeSurrounding("{", "}").split(",").associate {
                val (k, v) = it.split(":")
                k.trim() to v.trim()
            }
            return City(parts.getValue("name"), parts.getValue("pin"))
        }
    }
}

fun main() {
    println(City.parse("{name: Hyderabad, pin: 500001}"))
    println(City.Parser.parse("{name: Pune, pin: 411001}"))
}

Output

City(name=Hyderabad, pin=500001)
City(name=Pune, pin=411001)