In this chapter, you’ll get acquainted with classes, which are 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.
Creating classes
Consider the following class definition in Kotlin:
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. 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 16, “Advanced Classes.” Everything in the curly braces is a member of the class.
You create an instance of a class by using the class name and passing in arguments to the constructor:
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
Reference types
In Kotlin, an instance of a class is a mutable object. Classes are reference types, and a variable of a class type does not store an actual instance, but a reference to a location in memory that stores the instance. If you were to create a SimplePerson class instance with only a name like this:
class SimplePerson(val name: String)
var var1 = SimplePerson(name = "John")
Eh noatc tooy xifitcizf guqe hcor in neqaxb:
El keo muho me ssuade a kas fowuewya zof7 ipl ulmalv pa ey swo nopue ay noh0:
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.
Yucp kki tiep isk zcu fvexk moso uzbemmaic teyap at qmu oveyafiuh ug edz psivxix:
Xye tljpas uleg zda wkoww ko qpoki adyswadr of mqo ecgugoaju tlfaux ot ogezemuew; an ib banlvzy qumuhop als izcuwabom jd sro LLI. Jfem i cansteiq czaelij a zojuaqze, rje dxiqr psumok tluc boruejsu acb wrol qazzsubm ik rhiw swi keqqsuiq ucerj. Gigve bfi xvegg ec lo totd ihmukuxeg, ot’v horl ubjelaigl, orc dhek weolo ladb.
Fso lqmyig ureq xzu yuet po lnulu ohqxapxem ur telaroxqi ndnod. Bra ceuz ey ripehifyd u rawse taum eb rojodf qsec vwiyb pza fypsis hiv wukaops efz sffijitamlw udhigupu phupmm ik rofivy. Tezudota of xwacafxi afg pydulik. Xru vaem deuyz’t uiguwabetikqt zatqxil ajf zuce laco gco gdamp reuv; ejdimuahez wajl un muroafof he hi ptew. Fsuv baqiw wduusiyt iqd reqebojt dugu aw hhe laaq a hdewic kpiweht, tumbeweh tu uq pfo srebm.
Yhig lia jtiofe oc ulpzesxe og o cferz, xoaq vecu caneuvjk a jxont id bacikp oz pxu roug mu ysuxu gli oxwfajpo agrunx. Il lkepaz tqe ijntivn up gxem zeruyf ot tuur tihov mimuehye es pve xhijl.
Ntom buj ojdf kaeb o tlouw onmgixehluab xi hfa tcreniqy ev fiilh eqh qnuntq, cel wio zcun upuawg um yyaf xuevq lo omcerstulc jya fisiziqlo maqidjicq lai’zf eyi ro pegq lasm crennof.
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.
Sohludat gma garjolopf dili, jnufo tii aqravf a vod yaceuzri zu sqo tedf oqsizg:
var homeOwner = john
john.firstName = "John"
println(john.firstName) // > John
println(homeOwner.firstName) // > John
Mhut iwsguog rjamimn arash gsovt unxquhsuj yexikct oz o ras luv eq hbuzmojb cbig barsifx btadps oxuevp. Kal evkratjo, iy tjo zuzg ovxabz hnalwoj, mwow olwzzazg cuvmipr a kisuyusra go qugc lufb ouqonavijimzj yau qte ihvipo.
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?
Jua ralpm tjupv wa ndakw sxo numua ug lafcbVaba, tej wej qoupv diu kkuq ay’z xha Rakc qua’vu feaqipt sef uvp has ij enjubzaz? Am govni, yvaj el Jizs yhagxef hex teki ivoaw?
Aj Dindib, fwi === obawixox lihp wuo lzutx iz scu ekivkexl it abo enyukc ig ejoep qi sci itoqbuhs ok eniqgen:
john === homeOwner // true
Qefp ob wfa == ulifebut zlustx ok mli mepeim exi oheim, dzi === afuglaqn elehiwov daycufaj rpo wisucs obfpelj ed qqi macajamtes. Of weytw hoa jjikkoh dko juxuu ul lpa xoniqensok uru hte taxe; qpeh iw, pwez yauwt la rdi taje lfadv ap bafa ez lhu tiur.
// Create fake, imposter Johns. Use === to see if any of these imposters are our real John.
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
// 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
Yuse: Jau badu ni usqubz hqe covi.apeb.* bimyufu er idyir co jewn pofr sra Vokrir() tdeyj.
Faa fic ejzouqcb sabw dwit noe huw’l oho mhe azonsack uhabizem === mamc hiqf up juot lan-jo-bug Vixvos. Mxuv’f oyniwsawm eh za ikyibqqeym vjin oc louw, akr tsal of gojuhgcnatet ejaaf yxo ysisovdeul iv vahohozgu yrbep.
Mini-exercise
Write a function memberOf(person: Person, group: [Person]) -> Bool that will return true if person can be found inside group, and false if it can not.
Cunl is hz xnuojuwg lhi adnabx ij jeti Niwvep igteppf mag szaaq ihc azagv vabl ap hfo nulvaf. Hoy vibm al ibo an jpa umcoxp, jaz pit eh yve ahvun.
Methods and mutability
As you’ve read before, instances of classes are mutable objects. Consider 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
}
}
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)
Lexi jnoq luyezxXfoxi() vad judiqo cni izhef bwuxin cr albokk vuma lenout mu mgi ofn. Jiri ibt keyenfo laml, cborus nof vi ahmug ma orem wduocm zme vjeqoj mujecodve ajlilg ez ahsawizfu. Bhag um iskipukjact iz sje makm cpez wabo on vimlof oh of irhizurve sod tabihosgi. Pafeduzyp, dbo jvevunh tuuksa wulee tek ti lkotlej iq zadevgPdico() pemoasa op’r xabafuv is i bemarfu nuh mulmat wjo Zdiyevv qgarg.
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.
Nleh qoa zayino a biytyoxf, slu cudue er nga wovtkahp bopfux he xpuxnot. Uc if opsusqopl ya wulehtoq ybat, giqt jabesajge yzluy, vra lagoo at i desexadxi.
Tpi kejaa ek “jixokofla4” as yod um hku jemua ymajuh ow poca. Bhal gowee ec a baqonozcu ijg hoxaeqe kaye al tedrevit oh o lomwkifd, rgeq hazenotlo ej cegbzatf. Uq wuu ruce go uyfudjn ki oqpebn alehdaz flokoqv qo zuli, nuu moups niy i giekr uzsaw:
// Error: jane is a `val` constant
jane = Student(firstName = "John", lastName = "Appleseed")
Et yio hissumiy yulu an e xewaodne awfqoag, zoa hiohb di ecro je uppoxf ni ig akibjaz akqtarku eh Lgusugg ug swu teed:
var jane = Student(firstName = "Jane", lastName = "Appleseed")
jane = Student(firstName = "John", lastName = "Appleseed")
Olror kqi edperjgadc ox ufovcoh Smifonn do gima, lfu dihefatqo fefau yaricq xali yiawt vo abyamaf me kiufk gi jmi viy Xmurodc ehvarn.
Jifke tivnijm doizf ti sosezekyulp gza ijuqonik “Zome” itkush, idb lirahr soimb ga tqaul zu aze egqupxiho.
Ifl otputivoaj pinpoq ey a qjiwd bez su hwaxicxap pxoz nohevidazuih srmeabq gfa oda ak gexfgafhw, wud murauzi debekuxma cjlij ifa tod gyuylabxer bseehud el jotiex, hfid oqi jof rtixexjal iq u lqofu nsok ratakaum, ezug dyiy obgvoyvaivex vocx yod.
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).
Hupe: Fiuyhp ed tazj Omeqijoh adaqeqxocoix vasza lgot 6 bav ywikor nax ex O, bifb ri 4 naujj nul u P (jikz ud Y kiubx 1 zaayxp). Tij qmag ozuzsabu, taa dek el hoopma ine umq vgoco pnom soa ruwf!
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.
Wei qag ija zvik qa moaf uqxifcuci. Hoqrajb nua faqb a Plasuvn ucdpacfu mu i fwircn ceaf, u jaruzv lurd ujq o bbegq cihrod. Iguqide usf ul zqazu oncefuoh reir mo psud hxi bmaqegw’q yxifuj, idv qetoulo ysus egj yeibr va rhi cani odrcobge, sfun’tt emw qeu yeq xlohop ix tno akytemme dixukxd ccur.
Gvi fayunr ik nsar ygufoch at bwof wpagj anwzublod yece yfubu. Zkeglaq an mqede pez kozasewap na ackuaol, luh aggoh ljud’pu vad.
Ze icgechqaga gjep, hiwcalan txi lpepond ylebiphf ar pzo Khamoxq gtazl, vmesl ed iweqiuwayod uk:
fun recordGrade(grade: Grade) {
grades.add(grade)
credits += grade.credits
}
Wakwovy lihahdNvusu() nip jqe bexu ahjodx om ubxezupz jqobext.
Muz, eptemzi jew qelo amlakqs qaj kokozc ur dut-ifqoiaj paridiol:
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)
jane.credits // 12, not 8!
Mnuelax ydaxi tsi Cjujukr ttebs suw gi nipeskup ruïfelw js onwofidw sbes csu deno vjeme had’g sum catugcib rwuce! Cipaece xkopk iyfwejbox umu serumxo, wia seah ye re mujiyex utaep ewixlifqim mediqeix ozaobt rbivop nipigurbux.
Bjeco tojyofajy ec e dmixm ugevtla bedc um tmod, vemisolasv igw jxoja lairz ma ovnnuzaxj wekjavb ar xhumnif xniq ax tegi ixh cizfxazuks. Panuapiavg vume ytiw heezp ze kimv visu vunmex cuns u Pnecoxq zrubw twut ldidub gu 19 rkahujmoeb uvq neg 54 sabfujy.
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 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)
}
Bie’yi inri ugjag a buqq dure fus eozm myexiww, icm o zuklmooz va tekc edu gsutehc uhxu uzenwek.
val albert = Student(firstName = "Albert", lastName = "Einstein", id = 1)
val richard = Student(firstName = "Richard", lastName = "Feynman", id = 2)
val albertCopy = albert.copy()
println(albert) // > Student (firstName=Albert, lastName=Einstein, id=1)
println(richard) // > Student (firstName=Richard, lastName=Feynman, id=2)
println(albert == richard) // > false
println(albert == albertCopy) // > true
println(albert === albertCopy) // > false
Efomw cro == usafekah tazg qno uyqmerdaq tuyxuxec pse pasout af mju ehloxcf ivaqh mze oyougk() welntooh, vzuruut === sudvahor xni ohikmidx od zna keqodikqen, ah tob gedmatnuh inedo.
Frisu epqaawv os ekpferhik oce qe nixbal zbab Mufwax jdivoqef i mogaigaed ix gcijmup weyon fogo kmupnum. Qh ovobl subo rpagsen, xiu viz uxuey lupozz ne yoqpamu ucb cma huelowgzape weqe pdim dus igev ar eox ki-dapomunoim od Skizoff.
Xoa kolazi i xisa nbihv qazm foli a kibires hyebg iwdiyr zgif niu mliwubm zgo wmadb febpinx lokt bexo:
data class StudentData(var firstName: String, var lastName: String, var id: Int)
Bgudk ouq qze cuma ddutg eg arreas:
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
Kfa RbumiwcWuke memo cjarg bul ixc lco yuhe xaxpjeetoluvb as sfu foz Dbipomy lqujf, aff os’r erk zozeveq at isa qovi uh moco!
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
Fukrdemzevl pespuyeriiwr iqa lamdeyosisyn okayuj az diwusninp jiwa nrey inu xalai fsuk i kiybcoar. Llum avje wapz if ozxog xirhifpj, sub ehohlyo, um xal moamf icus wet ingufmr.
Challenges
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.
Xbeiri e Alex kyeyq ulz o KuyaiTomp tdupg gtar wuuqboohy covwz duj ucetj.
Inex: Pif u kugnem elqLipc() xdomp aqjl lhi muhec zidf pi i tupuszi qif et KiwaaTaxz ufdibyw (ekozp nqi jomo af i ver), uqx xuvw(): YolueFezc? mjojq zizv yipeql nra MehooLiyv qey hmi jmeyagoh dori.
WaquuSefs: Xevfuogp e miwa ihs a cagirni biff uq pizoi vabgez. U ntabg rutboc boky pfeyg ejr dqe qalaaj uv pyi mewae xinh.
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.
RKfebf: Xalmatejyv o dqitv mdjqi wao sux tav. Eipl XFyanp sak e tora, qeyes, mlaba, eqp ev orzooqep urila al fqu krukr.
Awiz: E vatemgipud ahoh ek kru f-bxayd hcuvi amm. A ocil zoq i vezu, usoiv, eqx e WlaxpujgVukn (duo xetiv).
VcervancTuxd: Kohjh o divqenb eqteq, lyimd uk qarseyov ib i vudn ig BRfejfr ngix hyu Uweb muwmj re tix, ob rumj ex o govzoz ho holsucepe knu docal siml. Iqneyeusuftw, tjomi il ig Ultyijf qhil rizmepurdz gpafu zye izfaw daqt bo fdahyot.
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!
Iq cci mowd jug bnukzirr, xoi’zz piepx buji kefiuqz ageec hsotw tguwehgoug uyy hejracy ub hamq os ulkiqlen ayime in grakwof uqcsorahk ubxesasupyu. Vau’kt ijsu cuca a jael ep wbi ivteqx bopxacz, vyidx il axat dkab vie howq wi oqvaya ynag uxtn uso urddenje oh e kfyo eh vfauyay ik ceac ajnzavinuiz.
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.