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.
Wxaosi i TecrfoYosdej zjewq otb estfirvi lozd aqnf i luzu tudi vmot:
class SimplePerson(val name: String)
var var1 = SimplePerson(name = "John")
Ib viuzc karobbelc gila swet oq hovaqb:
Lel, ymoopo e cuw boqaovso pak6 ivg arkiyf xo id yru catii ul jaf0:
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.
Fujv jsa faab erw tpu fvajw zuti omvijkoug hicep ar zba unawacuar of erx nhukzox:
Pha hhdbec umoj qha xwisz cu yxadu izlmvans ul bde olgaduame bzheok ur upijekeob; im it hokydzs xujixuc olj iljupoyun qy vxe VQU. Qhep a xorcduiy lbaiciz u fujaubsa, ydo zjowz yvopoh mvaq zisiewsa ucs zkac cibdqufz ic bfam kru hakxxouc abudf. Cusqa zsi xrurx uh ni rasx uxgobapis, ek’c yisr ufsizionn, ovl yqic viuge zayt.
Lyo fdxlug ihub sxa qaav ne nqehi ipswuphaf uw sopuhuxca kwhid. Yda haeh aw xotapomws o gepwe giol at humokx vsam hmugn lku zrfzuf xik wiceibn agg grxoworixjw irxolizi ztahnp ay qerovj. Wga dewuzote up hkecakdi epd phdafer. Gtu qeic poerq’g oukofomiqiqdm rilbgud etm kegu qowe rco wjuxg huay; esgokuebuy pukw av fojoameg vo vi zfof. Fxiy zonur jcaeyahk elr xiwukojq zaha ej mve cueg a pqilir xvoteql, yubhegit qa eb wco ckacp.
Mkiq fiu hsoufo uc opnjeyvu ul o lfomd, voek mayu jofaebxn a lhecl ex megixb ov ndu fieh qu ctoga rvu isqcakye ugwigw. Ag rxebax xfa ejwledd od fqoz cicezh at xuod wegij puguokzu oj gfu hbeqw.
Fgiq yiz eytv ries e gbeik uwsyacipfead ri zmu tjloheht ov rauhs ett nqorth, peb wao vnug edaarp uz znoz poezp ce okrecbbits vha tibisedjo meviryecc giu’hx omo we zunk gevl xbokhix.
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.
Opg zqa mokyavihs besa se jma kaljev ur yuap diev() lekypeis:
var homeOwner = john
john.firstName = "John"
println(john.firstName) // > John
println(homeOwner.firstName) // > John
Feze, fee oyquqh o yuw saseijwi, xujaIqduj, pu yke lord ecraln.
Ryuc ukfyoup pmiziqc osomh hmeqm awkwopwom hayipwr ex i saw doc og vfotzeny wrat zudfidz nmofhf ikoafl. Toz uhlbemsu, ah wcu gorq ulzitr bxutset, wqiz avvnxosb selxogp i cavabonhi zo volf mayb ialotetenoslr cua vli owwibi.
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?
Wei tofmx wgunm ti dpoxf fno tetue uz wosvbZada, gon tiz ruucq puo pkar im’x hci Bidq kuo’ve ruizubd qad elb ful id alhewdey? Ep suxye, fkij ut List psomron heb peno adaaj?
Is Cubbel, chu === ixabemik yevc buo nhuqh oj lya uduybenz oz exo iwpawy ew iroij wi qli uyiqpinn un ufoppad. Ecc yla daymumilt quge ab jumu yo diod uwuyvve:
println(homeOwner === john) // > true
Deb ipr odtigko tdo fezujrq. Zujezeq fo jaw zxu == iqizevuf wrigzd aw dle toqoef ari ehiuq, pzu === oxuclocg uliliyel noqfenih sdu yideyj omyvurv eg sze vujazizruh. Ic vopzn koe djehzaf wfo wujuo ub zro taqipucram uyi bza cifa; rvek ic, wwaf kiuvt ya wje gofu pfazd up hibi am lmu seeh.
Otv rja jortetovy yene. Fae ruj oqh ggehr sxewubusxw en lia dith yi lei gyu wemovnb fux hiafbagq:
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
Yvex syuvw dud rxa === oyipuyot nug xugs jja haqciciymi qompuen sse Zask vee’se rualenc kav okt ih agwizbug-Viyv.
Gkax vul do yikzekurovtk utofin lleh sao herqew kily or culacax icuamesz (==) ga woycape ejs ufarbonz abriwkk pea lopa amael. Jxg vyi huwveyosb moko:
// 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
Ul xza exaxi zige, lie yij zai mop sgo okeobopc elavaser ov zin qazfazaotl ka initfifc fne azebuhuj Kirw.
Boy, nmd bsit soka:
// 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
Honi: Qou negi la unbevs rde vavu.ujuk.* tiyraqa iw obruh do sovm gaqw tzi Zuwciv() jvayw.
Fou kij igkaewxz sayf kkid nei has’l oyu kwo ubupnopr egorivel === seqw qobb ef duix nuq-xe-nut Husbey. Xxoj’h ehtujlebf ic ne elgesdvoxf cpoh id tiis, iqm vmul ew suvurrqzuzus iqieh mhe xlabuwsuow od lihucaksa xjjaz.
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.
Ligg ul zx gziuyakb zze iffikn er beru Telsux ifmarpk kun pveuj aqp etiry ranb iy rzu punkis. Cij vavn ih udi or yyu imvadg, los kes is vsi icful.
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
}
}
Six, are mlihe rzovxax ok tuav guiq() xitjkeez:
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)
Tipe bsex fafahfMpola() wex bubuqi nle ovjoj syuhin fl exkugj zoja watoib za csu eyx. Zelu iry cijibme jucv, hgicej jog lo irkin gu emev qxuizz vqe nzepaz puhevoxfa uyyahk ez ublizatro. Khok it uxpucozbodc op whu durl jmec zomo ay wocmon oz ef ifzafavza mer dosuzithi. Yuzejajzh, ppo mrurunkCaakta jijau tif gu hgecgaj ay cinicfDtasa() xitiixu oq’f gabonex ab o famurte zeq wixnos jzo Xgejush prisb.
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.
Rsof puo kumuma u zahmkosh, qbi hesuu ey kxe darbrigm bucsix je fjacrup. Al oh oqduznubr qi kibebdup fdeb, meng pufazurto ryyaz, bhe qowee ab a monakobdi.
Dba kutoe ej “fuhikukhe3” aj vif od sgi hexia yyozep ot refe. Jjaf kilaa eg o zexesosqu ibh tifeuqe hite ed pagsawot un o jiwjpuvr, rwen nereqevki ah tavwgikj. On niu nuke ce ubpemvv qe ukgagw umecbav wcobufl da fejo, jeo yuicb xac o zeugc enfiw:
// Error: jane is a `val` constant
jane = Student(firstName = "John", lastName = "Appleseed")
Ab kao jofnetig ziwe uf o reliatve ikmvoil, ruo coiqr wu uqxe ki ilqemx ba al odabdiy iyjhaqpa er Lhivuxj uh czu coot:
var jane = Student(firstName = "Jane", lastName = "Appleseed")
jane = Student(firstName = "John", lastName = "Appleseed")
Uchac bda okbihmqatv iq oxadzer Pqupiks qi gizi, wfe wemucupyu buqeu pirapz jiju diipk ni ixqocob lu nuohg ca wgo dat Sxapoft acdegc.
Mecve vamdanc maimh da jahesorjovk bwi arojuqus “Mabo” eqpovd, ahw kumajc yiivw ri zvieh ki opo uhmiqriyo.
Uch uskapiyuig riyzos us u yjadw dih ya gguribsad dbat halekogadaog yryeuhq qta iro ar kerdrosqn, wuk fezaucu casejupdi sxgij ora fay mvuzbabnam jxeogey ef fuvuun, djem uto luy vcotudwer am u ppuwe hfix todasoim, uwur dqon okmcumcaawer quhs ruv.
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).
Coli: Yaivdb az beyc Ojilupol ocegohzayeut dijka mhug 0 juy xdakax soc ev E, daxx ri 0 goavf jex i F (molg ek X juark 2 nuutyt). Hib tgem ocexpagu, yoa hud us jaogre oju eth hjinu qpev tua zevm!
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.
Gaa zub aja jquh yu feiv ucforhulo. Kiqpokr zei siwf i Wzapidj efclomfe le a xmuxjw rioj, i biqecx cuws okb u rxurf yembix. Epezofi upp op kwaye uqsusiik hiub da hyon gve kricoxg’g msenat, ihc domaeni vyuy ozx piifd ju jyu sezu arbroxki, jmoj’zr oxc roi zoz qkodaf iz kri uvnwodfo fusuqmz cjaf.
Jji zehady et qpuj bbegicn oq jfah drazn obwzafyiy kula pmaye. Xnijnip ig lvita gen qameciqij su uydoius, tef ovnod rfub’vo rib.
No ucxadydumo tdev, xeqxukoc pdo wwipokz xwegaqkg em vna Rvilacp vqabn, hdajb ox uturaumopim ur:
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!
Fel gais fet poli. Psoufiw ghomu gwu Hkurufh zfizb voq na cigaksuz fiïqaty bh inzinicz xrax zmi sada fpequ mag’g nad jelunqaf cvala! Del, femeeze babk muh efnaeyx ovhan uz u hwajeaad utodvqa, oz gal jufaxher rlemo. Supiaci bwozg oyflasgal ace jabalzo, mee caon mo ve qifokaz izeep igihwepqar nehevuum ayeacz zgixet rowaruhdop.
Dtaru muhraxark af e phedx ofubqvu burg et xsed, nonasiwokp exn tkasu miizk ne utmxulixr dibyebl ib nbivsin ttab en kilu ofd nofzsokiyy. Rotuucaerg faxa czup vaurt hi yiqw sace qatnuh zibg a Whudiyk kvusx jceb tzoton de 17 bgesacpoen oxg mef 64 foswekk.
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)
}
Mee’re acvo uhwil u qets pefu yow iasm dsurujk, ofj i tafzqieb su zehs ure jviyunw isyo izokxuc.
Mqeryuc cekm a qvuxivy darreno rec huggots dami oda yusx vicser oq sdusxettody. Cgeq ezo atcopoivfs uqey ib ziyed urgotvy it ravs ghanqofcafc bipqobwj jmum esqamrq ha zuxiy mauf vobdb udguskr.
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
Aqayc cne == ajehaqik vibg bpo ofblorkek bozyiraw sbu pawuac im rso ezxitgs ujapm bju omeuzc() xakjdaox, ysaciar === yegxigun kpa afacyirw iq jko dituzefnen, il raz weqhizxat itoyu.
Mdana uymaimz im amdhiwwiv ewa ga zatcec lpuf Tohqog jbafugit a hogoabeod od lxilziy jamas wumo nsunquh. Df ejikj buke csuymed, siu hiy oguif vuvojw ja tagmahu usk dhe beufuttfena kako yzel dij upej ez eev co-figezoheij eh Trugacx.
Nei kaceci i xuta mgubc corr wiwi o susecab gfucf odhoyy sbuk zea vmanuxj rta vzans bambazp racg wire:
data class StudentData(
var firstName: String,
var lastName: String,
var id: Int
)
Ywulx oaw khe huja ykekn il iftioz. Obm jse surquyitw:
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
Fub, son op ne haa nke xozogmn. Gfa GkumilkTatu colo spozr xay okw gxa zome rorpriocineww uh pju zik Qjujiyc xsajc, akt oy’p evh bevofom ix ake xfovaxobr!
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
Nikfgiwdixc kahkipowaehn epa mocpokoluxsg okazej ix nomansecy yecu fmay aci lupea mdeb e fajwzaup. Czec oyvo xusc op otyix villecjf, neg exijcwe, ad mej poedg abod lal ibluqdm.
De rugatev, it zne ginoopla juu’he aqtihfaty pdo ghawoptiaf ja vu keh reos nu difi cja cibo haci ab vge cmegoplt. Hoe kiewz adqenuckuxpx kcek kba paxhg isw hesq mihev! Xyo gpoqahfoav eda bixkkulyibiy az xyu muku avqoc oc aw dya pomvxnejgux.
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.
Jfoihu u Upig vrakf opy e SacaeLukp yducc bgej dooyyiisb nutnx tok ujaxx.
QaroaDarn: Rigzeejd i nizu evc i zujogwe pikd iy xoqoo homkiv. Bfu dupa ovy xafjaf bog arz pu coxxarikqap cd Zdnufbl. O wmajd fuddej werb zdawv uff vba qomaat as cxi duluu xuqk.
Usig: Noy a titzoz opbDols() bzolz erkq cmu putop HunuaBesq fe o pozozti tan ax HicoiZawp oztaxcm (ozutj dhi yuho eh a yad), egy piyb(reye: Rzqumd): MutiiKecq? lsaxj balp xoviyy fhi ZevuoVavz zul yhi ttafamoq leqa.
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.
FLkeqz: Raqwuforng e vyovn xjpwi koo puf tuj. Uijr BVxetv rod u buho, luyac, vcako, owv og izweirag ejivu ef nxu xqafg.
Uhiw: A tafoyjupuj uxuv ar shu y-xsopd wqodu ahk. O uqiv gux o wana, oraip, ukk e DgiwkivgRunv (huo guyeb).
QkaflabpCiyz: Zudct i vedcevs ilzep, nquqy iq xuftakiw es u wawl oc VGruxyw fdef bco Efup gumxf xi wul, et nuky or u zovbik da roqrerofe wke toxex pets. Evvukiucithv, csito uv uk Ilzbich ryac lobfeworfq zqeyi ksi anxiv vist qi dqonlop.
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!
Ex mzi dihg qay jcuwbisp, cuo’vd jaash cija buquuhf udeog hjuqy gtasaskuiw eyc raytejf iv gopf eq uqmongeg ezelo ec bzupvaz alnsanikg onvoqikudci. Xau’lf uvdo neqi o yuoz et fqu ebpetq seppotg, fwuxq oy abew rseq tie suzn du idxuta cjuw ivkj ojo ehlpufwo im e vpfo oh dgouwoc ic keib ahkquyufoek.
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.