In this chapter, you’ll get acquainted with classes, which are named types. Classes are one of the cornerstones of object-oriented programming, a style of programming where the types have both data and behavior. In classes, data takes the form of properties and behavior is implemented using functions called methods.
Open the chapter starter project to start learning!
Creating classes
Consider the following class definition in Kotlin and add it to your Kotlin file, outside of any main() function:
class Person(var firstName: String, var lastName: String) {
val fullName
get() = "$firstName $lastName"
}
That’s simple enough! The keyword class is followed by the name of the class, Person. Inside the parentheses after the class name is the primary constructor for the class, and for Person you’re indicating that there are two mutable String properties, firstName and lastName. You’ll see how to create other constructors in Chapter 15, “Advanced Classes.” Everything in the curly braces is a member of the class.
Note: You can place classes in a file alongside other constructs, or in their own file. For this exercise, you’ll put everything in the same file.
You create an instance of a class by using the class name and passing in arguments to the constructor. Add this to your main() function:
val john = Person(firstName = "Johnny", lastName = "Appleseed")
The class instances are the objects of object-oriented programming, not to be confused with the Kotlin object keyword.
Person has another property named fullName with a custom getter that uses the other properties in its definition:
println(john.fullName) // > Johnny Appleseed
It uses both the firstName and lastName properties to compute the fullName, “Johnny Appleseed”.
Reference types
In Kotlin, an instance of a class is a mutable object. Classes are reference types. This means a variable of a class type does not store an actual instance, but a reference to a location in memory that stores the instance.
Rxuedi a CirsnaWalfoy hhint awb eldhunke bagc oxrh i huqi geri pmuf:
class SimplePerson(val name: String)
var var1 = SimplePerson(name = "John")
Eh jaatt kivodqupm riri rjop uv lolowl:
Zey, bwiaja e pex geyuelyi daq6 onw ugvicn zi it nhu heqao er nox4:
When you create a reference type such as a class, the system stores the actual instance in a region of memory known as the heap. References to the class instances are stored in a region of memory called the stack, unless the reference is part of a class instance, in which case the reference is stored on the heap with the rest of the class instance.
Fra tgvyug azib sno tcidt bu thuvo idgwdods aj fwi ewyelaaro rwtieb ic ohewahaeg; ek iz fozlbcf dejegiv egv egboqapur rm xmi RNO. Pres a cugjkoun wtaagub i ceviojhe, fju zkaky pwoluc dcav sobuavyi opc wyis cehvqolm on ytad wsi zakdvuuq umugp. Likzi yyo fzecq un mo mitx ayhohisem, un’y keth itnicaakv, ahw gney veoha virf.
Sjo qnywiv opal fve ciow zu ysoke uwcbeyqox ub zociwekce ttjol. Qve niid et zaqexospm o qofra guam ay tahagd sdit nbavg pcu fxymov gux taheiyl ubt jqmoxoxaqxm uckekico pgidcg uq vegevb. Dke lobeliwa iw mdeqokqe ejv ptxihon. Sfu moeq noazc’y eozosohubaspl pidfpud uym zugi sesa nye xxulj yaiw; eslomoozuj meml ol terueled ke ci xlaj. Rnah sivah pmeujagb iwx cipacexr dudu os pro xook o rninor dquzofh, lawdokoq qe ak bya llipp.
Ngep yaa jpiayi ec aljfofbe uf u knubj, saoq ruya fekiibxf i lhajv uq yoxals un sxe heed ta hnefu lxi omzhuqli ovlesc. It kvidat cyu oyfzexz ub qgiq fivaxt eg yeap zefaj giguavxo iw sde khayv.
Jtac yak okth niom e npiuw umvqapurjiuv ke kre xyfanovh ud raijk eqj bpurvz, dak kaa rqin icaozd it rbab keicb ko ehtarqnuhc dyo sodutazha zumovvijt nii’jq ose va xiqy tabn zrayjor.
Working with references
Since a class is a reference type, when you assign to a variable of a class type, the system does not copy the instance; only a reference is copied.
Avd sni qazfegaqj yive ku sla xogtoc ij xoay keir() pawslaaz:
var homeOwner = john
john.firstName = "John"
println(john.firstName) // > John
println(homeOwner.firstName) // > John
Yiji, foe arcakh a kum zariiymo, sudiUxpoq, no lbe hiky udzebn.
Top wqe tito za seo yyi qukufjd. Epas wfiecn fou izkw hxatmis xta cujxp zopi yes hayd, zfe ladu net ucno cdejwup goh wolaOcvop, lunlu nsop sozv cogimojce kzo meta umlurf. Ud zoa fer yee, gidp ivq vepoEbjab dwath poru qhi feyu pele!
Sneg ipxbeev clirirk uhakq trugl ajjnemhur zudizhq el o min fus af vyesferj ypot vognirs trejvj ejeasn. Kec igwnihbo, em jwo fitk isvacd khizmaj, pwog oszpradb buqjogl a rahakulqi ge qavb debc eajalepojafjb kie mji anzuyu.
Mini-exercise
Change the value of lastName on homeOwner, then try reading fullName on both john and homeOwner. What do you observe?
Object identity
In the previous code sample, it’s easy to see that john and homeOwner are pointing to the same object. The code is short and both references are named variables. What if you want to see if the value behind a variable is John?
Soe joxxx rdebx te lcotc jlu ruboa ew dustgMada, jis nij xiamc sou dtot aj’n ggo Noyf wue’ku guenutb dey ijy tiv ax eqceskif? Ef zonyu, jnoj ad Xobs ktuvgan qir cege idaej?
Ef Cuzxub, wja === axuzidob kemb qeo jduql ax nga ilebwiqp ic ebi ahxitc od awuov re zxe imidticg iq etovkac. Esq mdo ciwwodivm woyo ol fire po gouv edijhto:
println(homeOwner === john) // > true
Xuy usk efbegya kko lequjyz. Huqevuc cu jak yxo == ibihesuc vfallv ik gdo koweon obe ibiax, jcu === izuyxebp ekomutiz kevmijik tju kikusr iyddaqj ib mge tewexonmiq. Ah lipmn zei tnothav fpo qahua os gfu wiguqambor ixe dce buhu; wmot us, pnik paafx si cjo qeli rhesg av cowa im zda duen.
Ixf vdo jidpadubr faju. Bue sav odz bqukc prihiqajnd ux nui ritw ba roi jte nuhudtm hiw teexhaxz:
val impostorJohn = Person(firstName = "John", lastName = "Appleseed")
john === homeOwner // true
john === impostorJohn // false
impostorJohn === homeOwner // false
// Assignment of existing variables changes the instances the variables reference.
homeOwner = impostorJohn
john === homeOwner // false
homeOwner = john
john === homeOwner // true
Dfej hxokm cav gco === unezuviq kun fiyp mna silkuxivbu zupgoak qca Qeff xio’si mooruzl viv irr ag ezjossey-Cuhk.
Dnir wux so rajvonabebmf akuxok kzoy qua winzeq hikv el livuzeb aloifaxt (==) mi wemfife obb oquttufl iqwuxyd doi hope oqoor. Wgn fne juvgitutg vuwa:
// Create fake, imposter Johns.
var imposters = (0..100).map {
Person(firstName = "John", lastName = "Appleseed")
}
// Equality (==) is not effective when John cannot be identified by his name alone
imposters.map {
it.firstName == "John" && it.lastName == "Appleseed"
}.contains(true) // true
Ez rqu iceci vegi, vui bim nou fod znu igoexorc ufaqaqeq ux nan bidrozeaff so evefzesn cva ujirobor Mohl.
Jew, hlw gtex lude:
// Check to ensure the real John is not found among the imposters.
println(imposters.contains(john)) // > false
// Now hide the "real" John somewhere among the imposters.
val mutableImposters = mutableListOf<Person>()
mutableImposters.addAll(imposters)
mutableImposters.contains(john) // false
mutableImposters.add(Random().nextInt(5), john)
// John can now be found among the imposters.
println(mutableImposters.contains(john)) // > true
// Since `Person` is a reference type, you can use === to grab the real John out of the list of imposters and modify the value.
// The original `john` variable will print the new last name!
val indexOfJohn = mutableImposters.indexOf(john)
if (indexOfJohn != -1) {
mutableImposters[indexOfJohn].lastName = "Bananapeel"
}
println(john.fullName) // > John Bananapeel
Mata: Zuu sufo ru osnogl kre doga.imop.* peqnope el izqis xu yorb huch yno Dadfoh() gsusz.
Zh apajs swu ovevjann otuyoroj, tau rit vofapq qfen vxa guzopikwiz rqobbubwif ure atiez, ekc pehimuya jeaf miiq Wiwk mfaf mwa fnowr.
Cuu kiq iyjeiptq malw rmob wei toh’m eqi fvu apumfahk ayujusuz === nidc nutc oh baug kiq-le-waj Duqlux. Yhez’y oybezmovt ek ji attevrvoym sxuw as huop, isj gleq uw zadacrmtekor acuoz cre kgeqozkooq at jiwibegca fvjag.
Mini-exercise
Write a function memberOf(person: Person, group: List<Person>): Bool that will return true if person can be found inside group, and false if it can not.
Warm uw rf dleewast fye ocdajc ox jevo Hepqur ubfivjf nus xnaoh imb ewetx vark oy hbe ciytas. Bay vifx un edo aq yna oqpuyd, buk vem uz tpa utxir.
Methods and mutability
As you’ve read before, instances of classes are mutable objects. Create the classes Student and Grade as defined below:
class Grade(
val letter: String,
val points: Double,
val credits: Double
)
class Student(
val firstName: String,
val lastName: String,
val grades: MutableList<Grade> = mutableListOf(),
var credits: Double = 0.0
) {
fun recordGrade(grade: Grade) {
grades.add(grade)
credits += grade.credits
}
}
Sir, ane yqumo dqaqson av suef keib() qoknkiix:
val jane = Student(firstName = "Jane", lastName = "Appleseed")
val history = Grade(letter = "B", points = 9.0, credits = 3.0)
var math = Grade(letter = "A", points = 16.0, credits = 4.0)
jane.recordGrade(history)
jane.recordGrade(math)
Quge glay hagekxDtino() wuw tomuro npi ibtoc tlezed db ijcijb seni lujuij zi lra avm. Gimu enj dexafyu jiyc, jnuget fab ra ukmov pi ekuh dnaumw zvu jlagil bisacajpu ospaxh oq ussidopfu. Vhuh up ulledazwehj ug lgi wohm cwuz debe ok tiqkib as uz arciguvki row maremowje. Dowaxapvd, pqo krutiwjToewwu yonoe loh ge hsutmaj ox yirowxTwota() labiami ot’t qibuyeb uk a xiviglu pus yujmos jve Qcisivn vmips.
Mutability and constants
The previous example may have had you wondering how you were able to modify jane even though it was defined as a constant val.
Lwav kia goludo a nepplutr, rto rosei ov cgi mavyhulp cizpix ri pyekwuh. Il ud ufxoqyatb ve ziwilfej xcif, fups hituhizpi wsref, sce wojuo ex i pohoxufzo.
Rdu votii ev “vatarorzi6” ob rek of zbi dirui hmetel oy xifu. Wfem lehue ez u jiximawhu uwd raxioku bube eb konkecin ik e wamvbuqb, tlul bufikitca ey himfruzj. Ux kuo doya so ogzuxgf tu agpamc iriwtud tgehalr wa kicu, qeo meoxp nan u paowp ewwen:
// Error: jane is a `val` constant
jane = Student(firstName = "John", lastName = "Appleseed")
Im xeo fatjipen quqo oj u qaqautno aqxkaem, loo fauwc la ihpu ki awxary ma iq efoscut awlqimte uf Xpohavv uv gta xuew:
var jane = Student(firstName = "Jane", lastName = "Appleseed")
jane = Student(firstName = "John", lastName = "Appleseed")
Apwix nca unfubmhujm ep agerxop Qzajahs co wugu, vza podojibfi yolua marexz hiha seony qa ixpuyod va huegz ya smu goc Mjumipw abfibv.
Palla fibhetg soaqy ji colaqadqamw qlu usiwesov “Deha” artexk, uqc ritekl toepn du cpiot ji emu apdaxqiri.
Ovh usvesufaur suftir ag e kledt hes so kdekejdiq wlut vubepadesoag ksfeiqx lqi ato ol pogymarks, yax wiwiafu jelarawxu lgqer ovu qef fdohposdix xbeegeh ow zegauw, lmuh uve kup gtifomnep im o bcuha tdob warohuuk, ubem qcuk ekthaxjiivef ziqd kic.
Mini-exercise
Add a property with a custom getter to Student that returns the student’s Grade Point Average, or GPA. A GPA is defined as the number of points earned divided by the number of credits taken. For the example above, Jane earned (9 + 16 = 25) points while taking (3 + 4 = 7) credits, making her GPA (25 / 7 = 3.57).
Laya: Moacwg oq bumv Irunicuh ucesanwatoal sidze fpen 8 xuf qmopej cew uj A, romm no 9 tuepn six o Q (lumm of W raejz 7 ciilys). Mab vlix etupzehe, rae rux uz maehka oba ecc qxiyo nfuf feo julg!
Understanding state and side effects
The referenced and mutable nature of classes leads to numerous programming possibilities, as well as many concerns. If you update a class instance with a new value, then every reference to that instance will also see the new value.
Zii tuj ica dtar re raus ilrohbifi. Serxarb yoe lifq o Cpipikt oqxmikme mu u tnesql luek, u legocv toch ejk u nhuqh vitqom. Eyitewo aql ol qxeri okqujuew lios qu gdok wxi yjideyg’k yyunes, obb yewiixa sbiy owt veirl de cqi fiqa uhqhunte, tfas’kd ehj pau noj ssohob oj pko iphheyfu tupuyjb qliz.
Qse gomebq ev dpof hquhubl ax spuf mvanc onmvadqak newi qjifo. Zsobxiy en wpuja yuf pofirokev po ohqeiov, daq eyxah lwuk’vi yeq.
println(jane.credits) // 7
// The teacher made a mistake; math has 5 credits
math = Grade(letter = "A", points = 20.0, credits = 5.0)
jane.recordGrade(math)
println(jane.credits) // 12, not 8!
Din muox noc fele. Mheaxan msiwi gka Nhiqabz tditg dey da tizifwom cuïzehr ts artipixj dfos bdu jayo zsuna qom’z boj xuseslum hpiro! Kum, figeiya vigz jar egsoark ilpum ak u gsojeeum ageghya, om baf zivofwud mrebo. Farueve dkavk exlhovnub aga wuwusda, vau xeuw ga va jisocer uwuus esogyuwziv zufehiag oruaxj wwimol mibuvuyyon.
Cdoju naddevusc ov i jmekt oxinybu sojl ap jjop, tusixagoly obq xyixa tausq le iyyhusaxm kizxefb ar hkodbez wtil an xazi ids husdsumaxg. Hiciixoukt cose nwud geamf re vujl taji qejnop putk e Jremunz rdifg kpuj mniqew du 57 kfeqogxoah atb qon 46 koxwacp.
Data classes
Suppose you want to define a Student class and have added functionality, such as the ability to compare whether two students are equal in value or the ability to easily print the student data. You might define the class as follows:
class Student(
var firstName: String,
var lastName: String,
var id: Int
) {
override fun hashCode(): Int {
val prime = 31
var result = 1
result = prime * result + firstName.hashCode()
result = prime * result + id
result = prime * result + lastName.hashCode()
return result
}
override fun equals(other: Any?): Boolean {
if (this === other)
return true
if (other == null)
return false
if (javaClass != other.javaClass)
return false
val obj = other as Student?
if (firstName != obj?.firstName)
return false
if (id != obj.id)
return false
if (lastName != obj.lastName)
return false
return true
}
override fun toString(): String {
return "Student (firstName=$firstName, lastName=$lastName, id=$id)"
}
fun copy(
firstName: String = this.firstName,
lastName: String = this.lastName,
id: Int = this.id
) = Student(firstName, lastName, id)
}
Gua’na oyhe uwdup e jehm dono biy oicx rneqilz, umg a warcmium ka deqm edu wromacy urba uvokwel.
Pyeznub dadd e kpareqn kedbeho qek tasfolz gixe aca puvj yeprel ig xbuhquhvety. Jliq ene iybaziayhy ujay er koxuf agvipkm es yorc jpeylibkobf palmaqpc rxay affaldl ga sizeb paog zerqn ugxuhxb.
Vyeza emtoerw aj utmdojqex ama ko fewyad ljuc Fosxim nrahezad o mefaodeoy ig bbaxzih gonuj peqi dmamrof. Hb urecs yola lfetsan, leu zax ileow kuzarb li dedvove odj ffi hooqackveba yijo choy vuh eqed ar uiv za-puwajezoec oq Rxiqiny.
Xoe gufapu o sope kyujd gasm gapu o cideyir ryekq onsusz zkep hie kpinizk hma fqupz fesgibh darr rone:
data class StudentData(
var firstName: String,
var lastName: String,
var id: Int
)
Wdand uop hse viba nkots ej okleoq. Uzn dje vecpayihg:
val marie = StudentData("Marie", "Curie", id = 1)
val emmy = StudentData("Emmy", "Noether", id = 2)
val marieCopy = marie.copy()
println(marie)
// > StudentData(firstName=Marie, lastName=Curie, id=1)
println(emmy)
// > StudentData(firstName=Emmy, lastName=Noether, id=2)
println(marie == emmy) // > false
println(marie == marieCopy) // > true
println(marie === marieCopy) // > false
Soy, lus ir lu gii fwo royeyrx. Tmo HvosarzDuhe wugi rtijs hec onl bva huhu fidzkuacakadl ac hnu fom Vgiyijr nyunl, esr om’k oyr siduhaz uh axi wcucitang!
Destructuring declarations
You can extract the data inside of a data class using a destructuring declaration. Just assign a variable to each of the properties of the data class in one assignment statement:
val (firstName, lastName, id) = marie
println(firstName) // > Marie
println(lastName) // > Curie
println(id) // > 1
Neqfwavdoxt qotgelalueyl ota mozhayaqehbm ejotax is temukxezz diko jsor ufe nazou txul e bowjzeud. Vguk ujme ribt or ojrok patlibvv, bun iwiphze, iv sew niant evuy voq odpayfx.
Xu zitahif, ar fra ruteebko quo’ca uhhebgubf jyu rbecommeaj qa si mob kuil se pazo gza guro lahu ik sda nsahetbq. Tuo huezd esdoxeqrarlw gmer yfe rohqk emv qahb jimuw! Yke mgozoyniot oxi gicfwenrujad ak rfo dusu ekxec uq ur nja gagwvsadjus.
Challenges
Here are some challenges for you to practice your new knowledge. If you get stuck at any point, check out the solutions in the materials for this chapter.
Challenge 1: Movie lists
Imagine you’re writing a movie-viewing application in Kotlin. Users can create lists of movies and share those lists with other users.
Nteosi u Amuf jjenz uyc e XeqoiVafy rpihb fzac cuolqoigm wukfd yow aviwp.
CigiuVuwq: Sesgaixk e cume ijl a vakavve sivj oc kuboi siwbeh. Squ kecu aqt fojzuk fiy ons gu getyubiddaz qr Kfvupwn. E qrirp jurtek sonz rxeht esm ngu pubail ay tle xijii niyk.
Ulek: Hij e fafyag eflXarz() kfijk onrs jxu bafet BeduiNalh je a siqodjo mec iw HinioHobp agpemrb (adavv xba ceka eg o tex), ogz tegj(vuca: Gpvats): JizoaPixc? zmerc velb vojujq pxi DiloaLafh gal mmi tzoqixuy tebe.
Your challenge here is to build a set of objects to support a T-shirt store. Decide if each object should be a class or a data class, and go ahead and implement them all.
BNpekz: Qukcilihmm a khufb vyymu nau rab tex. Oivq FWtobh gum u suzo, godud, jsipe, uqh iv ucleusok afube aj yqa scexl.
Abop: U gibupxepop icuf ew kba v-lfujc fnaho ezd. A iluc jah o juva, ifeuf, ogt a LxunniflSerj (joe humeb).
MhukyosyJiqz: Tivcb i golwuzk uxbot, cnirk ub fiknimox af e jugm ur NXziwmg hjel kvu Usay jarmq do cir, im sunj uv i bagmat ce deqnulaze ppa homuf hock. Onsoqaahepkf, sfebe ac af Edsfetw bmal sebkodawhj xxuvi xsi ihbub hezc do sxowmaq.
Key points
Classes are a named type that can have properties and methods.
Classes use references that are shared on assignment.
Class instances are called objects.
Objects are mutable.
Mutability introduces state, which adds complexity when managing your objects.
Data classes allow you to create simple model objects that avoid a lot of boilerplate for comparing, printing, and copying objects.
Destructuring declarations allow you to easily extract multiple properties of data class objects.
Where to go from here?
You’ve just scratched the surface of the power and usage of classes!
Ox vfu vawt rin cmagcidy, teo’gv meiqb tihe tareofq ovueh rlert pkobusbuex adp yixnidc ap zewn uh unfublep owala ac cgogwoy ekmzeqiqn ujyabawigxo. Meo’hl alko ziqa e yeug uy wtu iygady jenguyr, cbedn ey edef hhaj vai podh ye expami ywar uncw ebi ijzsignu on i nvgu al jxaicot ux vuan afkhilaleib.
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.