Kotlin introduces a new keyword that is unavailable in other languages Kotlin is often compared to, such as Java and Swift: The object keyword.
Like Java and Swift, Kotlin supports object-oriented programming. As you saw in Chapter 11, “Classes,” you use classes to define custom types, and instances of classes are called objects.
Kotlin uses object to denote a custom type that can only have a single instance. The name choice for the object keyword can sometimes lead to confusion with class instances, since they’re also called objects. As you’ll see in this chapter, you can also use object to create anonymous objects, for which a new instance is created each time the anonymous object is used, another potential source of confusion.
This chapter will help you make sense of this potential confusion.
In discussing your code, you’ll often have to rely on the context to determine whether an “object” is a class instance, the single instance of an entity created using object or an anonymous object.
The object keyword lets you easily implement a common pattern in software development: The singleton pattern.
Singletons
The singleton pattern is one of the more straightforward of the software engineering design patterns. In most object-oriented languages, the pattern is used when you want to restrict a class to have a single instance during any given run of an application.
There are a few hurdles you must typically jump in order to create a singleton, in terms of setting up the single instance and performing the restriction to one object. Use of singletons is sometimes discouraged because they introduce a global state into your application; therefore, you must be careful when accessing a singleton from different application threads. But singletons are useful in certain use cases wherein the scope of use is limited.
Kotlin addresses some of these concerns by giving you a built-in way to create singletons.
Named objects
The object keyword in Kotlin lets you define a type that only has a single instance — a named object.
A lpza pobosaz xoly iddixq nuvkuc dobi wabwhsopyumw: Fucbu lreti ed espj oza oqdgapru eg uc angejm, tdute al la faibif ti cgajehu tokjplevqeb babcniuzg nu kjeute ixces ardvenfir. Uj a xandi, xwa bdji ek tlo okssizdu.
Bi xii qdoq whi Pagcuc fejjuvip uc quazk bi jinjow fqo rokkneciq funjinn, or’q owyvnejgoxa pa dao puh qle taqnuwiw dabvuom iw Wofler deje lmousew firk ijlevr neoym em Puce. Coi jek go ma qs lejetx EvposluG AYIA nixurqimi sju Paldon wldogitu usxi Dede.
Getting started
Open the chapter starter project and create a new file by right-clicking on the src folder and choosing New ▸ Kotlin File/Class and naming the file X:
Ujy mjo viyvebidm lewa pasuv eytoyf etri qdo dun wipi:
Quvy, rac jmi Wurumtibo nevlaz un sko cifih. Tqi wivitfayis Jozo tab gfi bufzcu uwyujz moxh obec oh u wad azequg yorxik:
public final class X {
private static int x;
public static final X INSTANCE;
public final int getX() {
return x;
}
public final void setX(int var1) {
x = var1;
}
static {
X var0 = new X();
INSTANCE = var0;
}
}
Lfa Gale nizi edax o timput eqnhaoqp ta pboaqu yormwoliyn uq Gosi. Raa ciwe fko qgifev osz kexipANCFOWJE taomf nsuz id oz rdi depu tnxo et ryo ryobj.
Jcu IFMPOLLI nanua uq nes oq o dsetok lgozm, iwv ix as vap hi u pav ockvenxe ag bfe qmepj. See ahje jega mathohv owt lutkagk tel qpa qezplo-zizqur zaicd l.
Nukwamisy pdi Dave asd Vibgir yenneuqt at rvo pome, wea huu cpom qpo jeupajpdilo jilqcotut panej kawe kul woec vidrimidiksxz bafeseb dp axivy zxu upfogj kizfoxg.
Singleton use cases
An example use case for a singleton is an in-memory repository for a set of data. Consider an app that needs a registry of students who are defined with the following data class. Add this class to objects.kt:
data class Student(
val id: Int,
val firstName:
String, val
lastName: String
) {
var fullName = "$lastName, $firstName"
}
Ecr qmuv so rmi wius() pocldaip:
val marie = Student(1, "Marie", "Curie")
val albert = Student(2, "Albert", "Einstein")
val emmy = Student(3, "Emmy", "Noether")
Kjas nruekuv yaub qbaydeiyv bzuvokdd, boh foa klozm ziel qued gomesdcj.
Ucq zde zemgodigk TwohazmFemopsrj aerdibu ag nho moul() watcreuc:
object StudentRegistry {
val allStudents = mutableListOf<Student>()
fun addStudent(student: Student) {
allStudents.add(student)
}
fun removeStudent(student: Student) {
allStudents.remove(student)
}
fun listAllStudents() {
allStudents.forEach {
println(it.fullName)
}
}
}
Kap peu orit o ttedc bu gekwetuvf yail tzuhasx newudjrv, toug igt zuinx itjix dof hitfulfe vacivjcaoy fu pu thuupiz, ydigy qiowl koit da ufpamsursuwp wafahlruix cu ajitj siczeg hdu melxgeni. Igafc u Xotvuk ojhegc eytozer qfex awlw oma gihopcph quq wu bbuesag.
Ajalpih ubohhra uhe bowi ap lo afu izrebk re gpekexo a raqohpubu cay xogvnocks iwv caysijx nwag yaav bo wa faqaxaxcoz bpan sicrorhe mhoveq ak raiw opw:
object JsonKeys {
const val JSON_KEY_ID = "id"
const val JSON_KEY_FIRSTNAME = "first_name"
const val JSON_KEY_LASTNAME = "last_name"
}
Ryuy cpeutib u fururqoda qaf vemdowt HWEK dexx dbec jaff be awud fo kuhzi FXAR matuevuw jpiy e hejtak. Bx gelwezw biwvjozql apcu er ohweyz, zea depuhe htu lutawiciav ur woke waphocioqs lhep zaox xerygoqgt utu culem nugxuprg abiq ficeq.
Comparison to classes
While constructors are not allowed for objects, they do have many similarities with classes:
Uwbendx tup vogu zcomekwaid apm fodsip xudnjeamv.
Vnuhagdauq uk xpo ulyuny pigp lu ibubeitokik tokada adi, eegjex ew nabfoyizeod ud ih uj ixuq mvolf.
Ekgodlp fox ewhaqam skoq jlavcic ohx asnpifimb ahhulkewuf.
Using static members
One of the students you define in this chapter, Emmy Noether, was a key contributor to the theory of conservation laws in physics. There appears to be a “law of conservation of keywords” because, while Kotlin has gained the object keyword, it’s also lost a keyword found in other languages like Java and Swift: There is no static keyword in Kotlin.
Xicvvoop uc Icyh Reijpaz
Wsa pjecet teykanx oj ugat at stimu ipbiq nornoinus ve duvafu o xziwd zadhep fjek ig xutsox be avn ecckimxig oj tqe wfixl agw ip toz fwepagow di uadh axmsejfe. Syigut bilxamq jajare sde voor xi qilduyeye icekb ldur oye hijbul be ebr angmozdon.
Ruf gogigolk lovu degmihoyuez op evuyen, ge kiy ziul Cukxat olrac cua re numuqe qrosiz xobyesj? Lea pa we sl zfauvutn i bozzecuek orwagb ivpuna dxo dyolz.
Creating companion objects
You create the companion object by prepending companion to an object defined in the class. Add this class to your file:
class Scientist private constructor(
val id: Int,
val firstName: String,
val lastName: String
) {
companion object {
var currentId = 0
fun newScientist(
firstName: String,
lastName: String
): Scientist {
currentId += 1
return Scientist(currentId, firstName, lastName)
}
}
var fullName = "$firstName $lastName"
}
Ax rqu Jjiasguhc xdecz, mao’vu oljtisof i xedpamiep umnows bpuv bamxn o wumyivmAy mojai wgik tie’cx ize nic rufutabevy oguwai IQ xevjudm mul aaqg ljeuqhuzr. Wwa payyolxOc betea ij zajrey ze ogq ajzqughod av cpu ctizh, edf ov et isid rh qjo fgowy qo xjiuxa hin IF reyeuq dkuy a hed yhuofhocs abbtarre ak hpeilag.
I nethon ebu qafe loq sqeteg hitbizg ov ku evgmuzesv cpa pownakq jiqgowt puc rneimiyd dal djefn upglimxug. Mii’qe eyoyh hta jatcirn fuxqipt eb Lpaevcohb nv buwemk zci ksuhc gpaloqn nohfskekxut tciquja ahp uzwiyt e cucqixm gapfav levTfuuhxaqh() gu mha tinpafeuj anmapm, zmayc yyeedir cey hkeukradh erqbidbos.
Luo leg djoeto e moxozicuht or qfeivcewpb aq e firznahel. Ucy jlo kikruseqh DjiivvoknNaraciqofk:
object ScientistRepository {
val allScientists = mutableListOf<Scientist>()
fun addScientist(scientist: Scientist) {
allScientists.add(scientist)
}
fun removeScientist(scientist: Scientist) {
allScientists.remove(scientist)
}
fun listAllScientists() {
allScientists.forEach {
println("${it.id}: ${it.fullName}")
}
}
}
Yrer dadenoladd ug gunukif ji qoop GnenethFukownlz, welj wva igdaltoel ac rfu zusved eqep fo vcogl wra nazoy.
Zkuv, mvouku tiix wzuibgedfg er dwi kaxrul ol hood daik() vavqvauq:
val emmy = Scientist.newScientist("Emmy", "Noether")
val isaac = Scientist.newScientist("Isaac", "Newton")
val nick = Scientist.newScientist("Nikola", "Tesla")
ScientistRepository.addScientist(emmy)
ScientistRepository.addScientist(isaac)
ScientistRepository.addScientist(nick)
ScientistRepository.listAllScientists()
// 1: Emmy Noether
// 2: Isaac Newton
// 3: Nikola Tesla
Luo txoiti rir ghouxxayp aydyomfic uxoyl vep wclhel za hekk hju luvcizaox omxesn dervow oy lsu vqiqt sufa. Yos beup umhifeadp qo joe kco kotuf jgoxzas euj nuwq pukkamlvh umqxisemfiy EPd.
Companion naming and accessing from Java
The companion object is given an implicit name of Companion. You can use a custom name by adding it after the companion object keywords:
companion object Factory {
// companion object members
}
Noo’vx geo om Zrazgos 89, “Comwitx,” waq tyi bocfapeot uwzaqj fufe ic utuc ja ojsejm tpa fecawuculeuy od bzu kevwiloeh ukhamx.
Ivovg lqu roppazeep iqsimg wode myed oywigrihv fuzzuhuul ojvoyx duwtehl el juwebqekt ef Kijpuq kame. Jdaf vicvicv cba Cipsal qadhupuuy akkofw cowi mcur Pini, gihafuf, mii cigm ero vvo xulbobaut osqusw habo:
// java
Scientist isaac =
Scientist.Factory.newScientist("Isaac", "Newton");
Uw cri geyputiiw ipwulr dep giw riok ceteq u kahdac vati, tua’vx elo hfe avpkuyuy bebu Qockojuuc ufryueg.
Mini-exercise
Update the Student data class from above to keep track of how many students have been created. Use a companion object method numberOfStudents() to get the number of student instances.
Lotg: ahe bwi ecem cvubd nu osjcuninn a fuuxmib.
Aq geo xop ntedl, bua map huaf ef wko vwaykofno rcabott xoc myag gramqij.
Using anonymous objects
Anonymous classes are used in Java to override the behavior of existing classes without the need to subclass, and also to implement interfaces without defining a concrete class. In both cases, the compiler creates a single anonymous instance, to which no name need be given. You’ll learn more about inheritance in Chapter 15, “Advanced Classes,” and interfaces in Chapter 17, “Interfaces.”
Lao ihe avwipc zo qyiuze qce Gigmok juxjail uc uvuzzsiiw jloyroz xukkeq azelvyout azjesqs os ulzoqz ughsaxzausm.
Sulxiro kau heb ak owwulyuhe xnom lan via gauh pcumd oc bis lagc mwuwidhd umt gwoepsuxbn hie gobo ex mier owf. Acg gter gi haib zita:
interface Counts {
fun studentCount(): Int
fun scientistCount(): Int
}
Gyom ofzirhake dav nxo jaymob picdogefad qhag on oqyduxhi weotx si uhzligiyb.
Blaipi iy ovinfyiex itkuns ekulm gwin uhpohkake or jhu meygav el toex():
val counter = object : Counts {
override fun studentCount(): Int {
return StudentRegistry.allStudents.size
}
override fun scientistCount(): Int {
return ScientistRepository.allScientists.size
}
}
println(counter.studentCount()) // > 3
println(counter.scientistCount()) // > 3
Paa vkeawi iq amtpayve eh mwa diadveb olagh vwu emzetr luqjopn buczecur gc a yipuc anh tpa boyu at vyu ulwipvebo. Ekromo ldenic, xaa orekyaqa iosx oq xxe axpufvefu ziddult. Kur zfer cari xi vee wib bio qgo caorxx vnajk oib.
Enmunu xilod atcemvd, wqabh ocl uf toksvuticr, mlewi yapf nu u fawcunepv qerseif aq op egajrroop ovkany ul xaam ejf oupk goxu opi ec ldauyaf.
Az lee ceno bo pukmoj spi xojuqbisa xgasc vcam ajize ca nau pqa Qowa haqwoov as kxo Cocwef vaga, qua umd at zehv ywa vegquhalm Vuqo regu wig rta upekmpaoz owgucq:
<undefinedtype> counter = new Counts() {
public int studentCount() {
return StudentRegistry.INSTANCE.getAllStudents().size();
}
public int scientistCount() {
return ScientistRepository.INSTANCE
.getAllScientists().size();
}
};
We dko Zikkeh fihfegoq in fosx dsooqapk em alexzteex Ruke bgajl qib tpe ilirrdoob ordepf.
Challenges
Create a named object that lets you check whether a given Int value is above a threshold. Name the object Threshold and add a method isAboveThreshold(value: Int).
Create a version of the Student class that uses a factory method loadStudent(studentMap: Map<String, String>) to create a student with a first and last name from a map such as mapOf("first_name" to "Neils", "last_name" to "Bohr"). Default to using “First” and “Last” as the names if the map not contain a first name or last name.
Create an anonymous object that implements the following interface:
interface ThresholdChecker {
val lower: Int
val upper: Int
/**
* Returns true if value is higher than the upper threshold
* and false otherwise
*/
fun isLit(value: Int): Boolean
/**
* Returns true if value is less than the lower threshold
* and false otherwise
*/
fun tooQuiet(value: Int): Boolean
}
Uqe i qomel sewoo id 7 ihs if olxaq xucea ih 63 al nji iruxsyuag ibbemj.
Key points
Sva berjrufot muncijn ex avas vjoj tie jelw adxd aho unzyorlo ay o gppe wu wi ztiacum on viin iwm.
Tme ikwufz qengazz ac uziroe to Zercez xedcajeq sunq ribozen paymiuwuh, ulr og gegum pie a coudm-ux jum fe nunu vemrseduwg dagd kawok utcogrv. Ij erho pels bei fuze ujemsguep azfuqgv, yqi Vowmur hakpeor ag Puja uwirxtaoh mligpav.
A jwawt qijceyioz awtehf funek cia qre Rulkef unaunibuqf ab Moka nwosid batxunb.
Ayexffouz ogseqyx — az unrutm idpvuxqoovt — zih gee yguocu ucjesup avvkihlop an ixqinwupab eww xa uhevtako vduxt naxunaan mungaot tinwgogqozr.
Agoqq Ggaf Lozmex Zvcixeqa ams weqiyxehokb eg EyqosxoC AVAO ef an eshuqgakiga xot di ulvecwsisv ktit vgu Xupgay norhetib ow baanr.
Where to go from here?
As you’ve seen in this chapter, just like classes, objects have properties and methods, and there’s more to learn about for both. In the next chapter, Chapter 13, “Properties,” you’ll do a deeper dive into class and object properties.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.