Companion Objects
Part of the Object Oriented Programming section of Coddy's Kotlin journey. Lesson 13 of 57.
Some members belong to a class as a whole rather than to one instance. Kotlin puts them in a companion object inside the class, and you call them through the class name:
class Temperature(val celsius: Double) {
companion object {
fun fromFahrenheit(f: Double) = Temperature((f - 32) * 5 / 9)
}
}Inside main:
val boiling = Temperature.fromFahrenheit(212.0)
println(boiling.celsius)Output:
100.0fromFahrenheit is a factory function: a function that creates instances. Unlike a constructor it has a descriptive name, and it can return null instead of an object. With a private constructor, the factory is the only way in, so it can check and clean the input first:
class Username private constructor(val text: String) {
companion object {
fun of(raw: String): Username? {
val clean = raw.trim().lowercase()
return if (clean.length in 3..12) Username(clean) else null
}
}
}Inside main:
println(Username.of(" Ada_L ")?.text)
println(Username.of("x")?.text)
// Username("x") // error: the constructor is privateOutput:
ada_l
nullThe companion object belongs to the class, so it can call the private constructor, and the class's own code uses the companion's members without a prefix. Here every ticket takes the next number from one counter that all tickets share:
class Ticket(val holder: String) {
val number = nextNumber++
companion object {
private var nextNumber = 1
val issued: Int
get() = nextNumber - 1
}
}Inside main:
val a = Ticket("Ada")
val b = Ticket("Bo")
println("${a.holder} #${a.number}, ${b.holder} #${b.number}")
println("issued: ${Ticket.issued}")Output:
Ada #1, Bo #2
issued: 2Kotlin has no static keyword. The companion's members play that role: one copy for the whole class, reached as ClassName.member. Like any object, the companion is created once, when the class is first used. Put in it what does not depend on one instance, such as factories and shared counters; everything that reads an instance's data stays a normal member.
class User private constructor(val name: String) {
fun greet() = "hi $name" // needs an instance: user.greet()
companion object {
fun guest() = User("guest") // belongs to the class: User.guest()
}
}Challenge
EasyComplete the class Coupon. Its constructor is private, so coupons are made by the factory Coupon.create(code, percent) in the companion object. It returns null unless the code is 4 to 10 characters of upper-case letters and digits and the percent is between 1 and 90. The companion also counts the coupons it created in created, which outside code can read but not change. apply(price) returns the price minus the discount, rounded down (price * (100 - percent) / 100).
The supplied code reads lines SAVE10 10 200 (code, percent, price). It prints SAVE10: 180 or invalid coupon SAVE10, and finally created: with the count.
Your code goes in Coupon.kt. Main.kt holds the supplied input/output code and cannot be edited.
Try it yourself
fun main() {
// Supplied input/output code: keep it as it is
val input = generateSequence(::readLine).toList()
for (line in input) {
val (code, percent, price) = line.split(" ")
val coupon = Coupon.create(code, percent.toInt())
if (coupon == null) println("invalid coupon $code") else println("${coupon.code}: ${coupon.apply(price.toInt())}")
}
println("created: ${Coupon.created}")
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Classes and Objects
Working With FilesDefining ClassesPrimary Constructorsinit Blocks and ValidationSecondary ConstructorsRecap - Library Card4Inheritance
Open Classes and SubclassesOverriding Memberssuper and ConstructorsAbstract ClassesRecap - Staff Directory2Properties and Encapsulation
Custom AccessorsVisibility ModifiersPrivate Setterslazy and lateinitRecap - Bank Account5Interfaces
Declaring InterfacesDefault MethodsMultiple InterfacesInterfaces as TypesRecap - Payment Methods8Generics
Generic FunctionsGeneric ClassesType ConstraintsVariance With in and outRecap - Priority Queue11Project: Library Management
Books and MembersBorrowing Books3Objects and Companions
Object DeclarationsCompanion ObjectsConstants With constObject ExpressionsRecap - Ticket OfficePractice on your own: Kotlin playground