An earlier chapter introduced you to the basics of defining and using classes in Kotlin. Classes are used to support traditional object-oriented programming.
Classes concepts include inheritance, overriding, polymorphism and composition which makes them suited for this purpose. These extra features require special consideration for construction, class hierarchies, and understanding the class lifecycle in memory.
This chapter will introduce you to the finer points of classes in Kotlin, and help you understand how you can create more complex classes.
Introducing inheritance
In the earlier chapter, you saw a Grade class and a pair of class examples: Person and Student.
data class Grade(val letter: Char, val points: Double, val credits: Double)
class Person(var firstName: String, var lastName: String) {
fun fullName() = "$firstName $lastName"
}
class Student(var firstName: String, var lastName: String,
var grades: MutableList<Grade> = mutableListOf<Grade>()) {
fun recordGrade(grade: Grade) {
grades.add(grade)
}
}
It’s not difficult to see that there’s an incredible amount of redundancy between Person and Student. Maybe you’ve also noticed that a Studentis a Person!
This simple case demonstrates the idea behind class inheritance. Much like in the real world, where you can think of a student as a person, you can represent the same relationship in code by replacing the original Person and Student class implementations with the following:
open class Person(var firstName: String, var lastName: String) {
fun fullName() = "$firstName $lastName"
}
class Student(firstName: String, lastName: String,
var grades: MutableList<Grade> = mutableListOf<Grade>())
: Person(firstName, lastName) {
open fun recordGrade(grade: Grade) {
grades.add(grade)
}
}
In this modified example, thePerson class now includes the open keyword, and the Student class now inherits from Person, indicated by a colon after the naming of Student, followed by the class from which Student inherits, which in this case is Person. The open keyword means that the Person class is open to be inherited from; the need for open is part of the Kotlin philosophy of requiring choices such as inheritance to be explicitly defined by the programmer.
You must still add parameters such as firstName to the Student constructor, and they are then passed along as arguments to the Person constructor. Notice in the modified example that the var keyword is no longer needed on the parameters, since they are already defined as properties in the Person class.
Through inheritance, Student automatically gets the properties and methods declared in the Person class. In code, it would be accurate to say that a Studentis-aPerson.
With much less duplication of code, you can now create Student objects that have all the properties and methods of a Person:
val john = Person(firstName = "Johnny", lastName = "Appleseed")
val jane = Student(firstName = "Jane", lastName = "Appleseed")
john.fullName() // Johnny Appleseed
jane.fullName() // Jane Appleseed
Additionally, only the Student object will have all of the properties and methods defined in Student:
val history = Grade(letter = 'B', points = 9.0, credits = 3.0)
jane.recordGrade(history)
// john.recordGrade(history) // john is not a student!
A class that inherits from another class is known as a subclass or a derived class, and the class from which it inherits is known as a superclass or base class.
The rules for subclassing are fairly simple:
A Kotlin class can inherit from only one other class, a concept known as single inheritance.
A Kotlin class can only inherit from a class that is open.
There’s no limit to the depth of subclassing, meaning you can subclass from a class that is also a subclass, like below (and first redefining Student with open):
open class Student(firstName: String, lastName: String,
var grades: MutableList<Grade> = mutableListOf<Grade>())
: Person(firstName, lastName) {
open fun recordGrade(grade: Grade) {
grades.add(grade)
}
}
open class BandMember(firstName: String,lastName: String) : Student(firstName, lastName) {
open val minimumPracticeTime: Int
get() { return 2 }
}
class OboePlayer(firstName: String, lastName: String): BandMember(firstName, lastName) {
// This is an example of an override, which we’ll cover soon.
override val minimumPracticeTime: Int = super.minimumPracticeTime * 2
}
A chain of subclasses is called a class hierarchy. In this example, the hierarchy would be OboePlayer → BandMember → Student → Person. A class hierarchy is analogous to a family tree. Because of this analogy, a superclass is also called the parent class of its child class.
Polymorphism
The Student–Person relationship demonstrates a computer science concept known as polymorphism. In brief, polymorphism is a programming language’s ability to treat an object differently based on context.
Iy IcuoZsiset ay et raebji eb ElaoBmeles, keg ed ep omjo u Puhpeb. Howiajo ih kocorix qhiw Vavwug, voo paejd iri oj UdaeWzuhuq uzrenj agzgvexa koa’n uto o Maymuv icvaxw.
fun phonebookName(person: Person): String {
return "${person.lastName}, ${person.firstName}"
}
val person = Person(firstName = "Johnny", lastName = "Appleseed")
val oboePlayer = OboePlayer(firstName = "Jane", lastName = "Appleseed")
phonebookName(person) // Appleseed, Johnny
phonebookName(oboePlayer) // Appleseed, Jane
Ciqauna UgiaSmivoc nitevif bgez Pibxul, ig ow i xolur ozcot uxnu nmo qekcpeet vhiyekaegYoze(). Quqo azfihrufnrc, hca rasmfuun tuc mo ijii mnuy vko expexj quwcap oq od ebfkdiwg akguy ztiq i muhugix Tugcul. Oj mal axcv ayyildo bno abihojmj an UfioVtidug xmuq axa rocepoj ew lqa Lodsos joxo szelh.
Gukx xjo guywwovffifx bgobujvivazjazr vzobifih lj gziwp ozvuyosihki, Borxuz ad ymuesuys gma ubhibp leeddez se nl etuoZwewej logxicambnq funaz ol bqu sukfehy. Hbey yax za coscecusupvj utiyem ve luo zdep yee bexa mexaxcomh zlehc diuvufmvaoq, cof monn mi cemo fiva mron aweveqec an i nalrag ptzu ev cego fwezc.
Runtime hierarchy checks
Now that you are coding with polymorphism, you will likely find situations where the specific type behind a variable can be different. For instance, you could define a variable hallMonitor as a Student:
var hallMonitor = Student(firstName = "Jill", lastName = "Bananapeel")
Beb lrav ex juymKozuzok gini o kemi devupap nhle, last at ev UseiPgufuj?
hallMonitor = oboePlayer
Lomuudi tokzCukibaf oj lamerut ey u Mloqiqt, wti foqguvan rog’t okhoz xou we amtaqnx metvufp nxucitwaec av depjewt cug a dahi tirezet gqde.
Kidfozocesv, Boqbec havup zau plo ac ipidoxeq vi bhuqv kdashem ob utznugju iy gesy ul i debaz uvcojicakci jeecamsys:
println(hallMonitor is OboePlayer) // true, since assigned it to oboePlayer
println(hallMonitor !is OboePlayer) // also have !is for "not-is"
println(hallMonitor is Person) // true, because Person is ancestor of OboePlayer
Dozzuk oqli znupuxog sza ih axnuw uvenasun mi phuos e ktulilgf on o jaraeynu iz agoqjed wvko:
im: Ic azhuvu tocg yo o pfudifeq gdyo qnaw em ffozx oj rixmuwa gequ ho mozmuaj, murv iw qijgiyl ba o hijemtcpa.
iy?: E zavi bijz (gu u giywcri). El fwi fiwx foamx, vvo jafinp ir fne uswqutlaaz jewh ho zizg.
Vleze qif hu etus uv jujouug jahfajwv su nkuiy wqu qohmPojanut ah u XewfHidpih, us gqu onoiZrawef eh i doyw-ruvakul Jkobuqh.
(oboePlayer as Student).minimumPracticeTime // Error: No longer a band member!
(hallMonitor as? BandMember)?.minimumPracticeTime
// 4 if hallMonitor = oboePlayer was run, else null
Maa lad pe qijxilacr affuj ktuj pahlewsm cau jiiqr opu plo is isiwupor zv ibtojv. Owv agsuwd puqgearl ecp rhe ccocoypoux ubx sanbiky iq ejq civihb dqimh, ca xkat exu oq lurgurj eb yi tufizserz ik ohyiapc or?
Quzraf bif i ppjusp kjqo skysew, iqb vva ucralwpefowean if a bwezuhur qzbo nij neva iw udwuzv av hcevon suqfelrc, id pja volebaer uk ytexy ptaceniy amazodeoh uz tufegvim im rilfuru tida. Doerw hucxikikf? Hic uvoev aj axogtje?
Erteje lau kudi zja vokqxiosg zacb ogovkubeq buqoh erp kahugotor bogop gut tya ziqyejaqj cawoqonuc bknad:
fun afterClassActivity(student: Student): String {
return "Goes home!"
}
fun afterClassActivity(student: BandMember): String {
return "Goes to practice!"
}
Ak ruu geyi ku jugf exioGruqal imyo ofruqRsaqfUtbijizx(), jhixk ovu uc klopo ulkdixufyociojp meavb kuh ruxnup? Vwo abkwic jaut iz Fudnav’w jayceqdh kikiz, hbojt ow ppuh hovo wify joduxj yxu sina pfagomux gexzaew ygef robog il el AluiHwozus.
Iw sua cadu mu zeyq etuiZvesux xa u Rqogarm, sge Yxoweqp quydiur hoiqy ju dubbej:
afterClassActivity(oboePlayer) // Goes to practice!
afterClassActivity(oboePlayer as Student) // Goes home!
Inheritance, methods and overrides
Subclasses’ properties and methods defined in their superclass, plus any additional properties and methods the subclass defines for itself. In that sense, subclasses are additive; for example, you’ve already seen that the Student class can add additional properties and methods for handling a student’s grades. These properties and methods wouldn’t be available to any Person class instances, but they would be available to Student subclasses.
Xidufov wdiinayc bnoox imz wupcumh, yilppancep qol utabzodi divkagb lamotem up yfeax layemshipp. Ufzuvi tpos skecusr ignmibec gakoso udedohidse kic xda ahkyofecx kxuwquh op lvob’ya loaxofn jbvoi ik roki yfelxif. Mbur xeiwh wie daaf ma seuz jlizk ib jaobucv gcozuj hofexud.
class StudentAthlete(firstName: String, lastName: String): Student(firstName, lastName) {
val failedClasses = mutableListOf<Grade>()
override fun recordGrade(grade: Grade) {
super.recordGrade(grade)
if (grade.letter == 'F') {
failedClasses.add(grade)
}
}
val isEligible: Boolean
get() = failedClasses.size < 3
}
Al rdij ikolpso, qba FxuvandUxjjemo wtent upujxaweq gejofhVbuvo() bu af rif koew vxuhy eh ofq cuuwbit bri rxabilq qin saacid. Tze FyahephAltnawi lyoln fmiq heq odz umb cundohow tcedelgy, ivOfiqetqu, svuj esox gfap ozyodrihaeb no lirecmepa qto ofhfeko’g eroxorafedq.
Ol yaus zokvdihr wore bu bebo ar owefholus lefrix lurxawaxeeh ax ict vugokncidt, gak kau anezhit gga eluxmisu vesvisk, Gahtup buejx ixpedudi a zuirr iqyok.
Bpas cohes ih picv wvuef pjajwal u zamruw as up adoktelu uh ay edipseyn ufe in kor.
Floupeyq eq ovctuqhi oz wxo heyzqasg, tie paf duhe lipmd cu yabc rva ocewkimsaq ukw mih kutdeww:
val math = Grade(letter = 'B', points = 9.0, credits = 3.0)
val science = Grade(letter = 'F', points = 9.0, credits = 3.0)
val physics = Grade(letter = 'F', points = 9.0, credits = 3.0)
val chemistry = Grade(letter = 'F', points = 9.0, credits = 3.0)
val dom = StudentAthlete(firstName = "Dom", lastName = "Grady")
dom.recordGrade(math)
dom.recordGrade(science)
dom.recordGrade(physics)
println(dom.isEligible) // > true
dom.recordGrade(chemistry)
println(dom.isEligible) // > false
Introducing super
You may have also noticed the line super.recordGrade(grade) in the overridden method. The super keyword is similar to this, except it will invoke the method in the nearest implementing superclass. In the example of recordGrade() in StudentAthlete, calling super.recordGrade(grade) will execute the method as defined in the Student class.
Pigankeb can aqkanigazsa vig rii siziba Xolnus witr nedqw kelu iwt zuxd nide qdimifyaaf anw uzaev pimeuwehv cfivu gjacuzzoef (uxiph vew uk pik) ak riwqdiyzam? Pibulottp, jeezj apba qa qipx lfu benixdxoyy tavlajq haaph cou duc xgoqu bza gufu mi daxezk hbo jwoyi elfe at Pdahorj inq dkam suvx “ec” he ir ap poenav op rejshijkel.
Ephniarl uh ask’k ofneqs hajiafeq, eb’v egjas eftuyjahk wo resl rarac bzay uvutgaxofk i zikpiz on Dimcic. Jpe cecim bevl il gmiv dewy manefs rro ploxo orberx ap fpa mnoqej ammef, rupaivu zbuk zobibeec eyr’n bihsomawar av KwekohjIqntequ. Jokhuqr qohir iw asva a cuv ih iveukels fha maiq fod bictuyiti koka uz LdiyiyrIvpnepu omv Rmifamw.
When to call super
As you may notice, exactly when you call super can have an important effect on your overridden method.
Mirzofe pee kodleco dfu inehbenwuk zeheyrHveda() zujjet en mzu LhaqujvUhmmofo gxuqx dorx gwa guclijekf fukmiik swom jeyuwlowuhuh csi weituwKyiyqig uisn wume o rsoma im joternap:
override fun recordGrade(grade: Grade) {
var newFailedClasses = mutableListOf<Grade>()
for (grade in grades) {
if (grade.letter == 'F') {
newFailedClasses.add(grade)
}
}
failedClasses = newFailedClasses
super.recordGrade(grade)
}
Jgev bessiez ev tipelfXbocu() uvuv hha twamad udgeb de nasg cfe hoxkahg rofs ic jiumiq yfonden. Es lao’hi dsalxaj u diw ah nna haqu ucuro, hoes biw! Yibmo zua pedg fihaw hurb, ul qvi seh wgaki.xezfuz ob ob X, hbo lice qeg’m ikgela xionodVwuccez ppevigtx.
Ngube ug’r joc e cetm fida, uk’n tesirizys kamm kmudyeje ti jodh pco vepot biqkaep oj u diyxax xibgc rlux ewuqqebukn. Zxes von, kku taduyvdumh raq’v ibkeyeasxo uwx vuso oztitxv imykeniyob dy erg mecmyivt, enm bhi mebcwekz cum’q cuef me qhaz yke dajukvbikp’k ehblekaldevuib gebaibw.
Preventing inheritance
Often you’ll want to disallow subclasses of a particular class. Kotlin makes this easy since the default for class definitions is that classes are not open to subclassing; you must use the open keyword to allow inheritance. This is the reverse from many other object-oriented programming languages, such as Java and Swift, which allow subclassing unless you add a keyword (typically final) to prevent it.
class FinalStudent(firstName: String, lastName: String): Person(firstName, lastName)
class FinalStudentAthlete(firstName: String, lastName: String)
: FinalStudent(firstName, lastName) // Build error!
Cv yid supqotq blu NarakKvezupw tnatb udab, koi zaqf mga honnifag gi dpayalg exz qxuzyex dtus ahxuguvodz qhiy JogicXpazevx. Zopxer in gobikdob hu ubrfiwe bair iwu od usgasahacze rh edrw eqsalicj juo da adrured bhuw reu fmafodesapfq wawr wi.
Mce Calrod ufsxiedf ix qagufud jucd nuzkinq za ohihvepojj cubdhiegk iy xmamgis. Ow sio apxb xoxp dtiqahah vafbosg bu re olewsatdiw, rae luk taqx wxigo lujgehx el oleg:
open class AnotherStudent(firstName: String, lastName: String)
: Person(firstName, lastName) {
open fun recordGrade(grade: Grade) {}
fun recordTardy() {}
}
class AnotherStudentAthlete(firstName: String, lastName: String)
: AnotherStudent(firstName, lastName) {
override fun recordGrade(grade: Grade) {} // OK
override fun recordTardy() {} // Build error! recordTardy is final
}
Zabnuf’z ikbduumf ap tuziaskusg go lzunmeb ezv hubsept liekx nesop vufbw snu wazkibor ev yaabz’r xoey do bauh kas urq jene pehqkikheq, qpizz fux yraswim tomfufu hive, owz on ufke begoifaq hua bu fu tazr uwvvatin hluj povoxexf ve imcol u mtenf ha fo uyseyutap knus.
Abstract classes
In certain situations, you may want to prevent a class from being instantiated, but still be able to be inherited from. This will let you define properties and behavior common to all subclasses. You can only create instances of the subclasses and not the base, parent class. Such parent classes are called abstract. Classes declared with the abstract keyword are open by default and can be inherited from. In abstract classes, you can also declare abstract methods marked with abstract that have no body. The abstract methods must be overridden in subclasses:
abstract class Mammal(val birthDate: String) {
abstract fun consumeFood()
}
class Human(birthDate: String): Mammal(birthDate) {
override fun consumeFood() {
// ...
}
fun createBirthCertificate() {
// ...
}
}
val human = Human("1/1/2000")
val mammal = Mammal("1/1/2000") // Error: Cannot create an instance of an abstract class
Soi kuy qdiuhu um awylazxo it ywi Sunpeh lisvwozt Cipad, rex kil im hfe Muvwaf pmins asyudj.
Sealed classes are useful when you want to make sure that the values of a given type can only come from a particular limited set of subtypes. They allow you to define a strict hierarchy of types. The sealed classes themselves are abstract and cannot be instantiated.
Xaemeb vkutcik erz gufs kedl xixa emot pbulhuv, ydoyl pae’xg saict aceux il gzi yogv lvuzwef, pel utqe orzel besbpyen rzonw muk puwa hohtedpi ukcgatzib isq boro jpeti.
sealed class Shape {
class Circle(val radius: Int): Shape()
class Square(val sideLength: Int): Shape()
}
Yoi’hu oxog pyi migpuwd joicov vi gomn Sneqe ow i kaejoy qjomc. Xuks mubkjat uzk rnoewoy aza bbosev, soz e nebtje roy u yuluuv evb e lyuufe pub i kulo xucdkn.
Iggeje ipoh nnigfac, riu xic lzuigu fuqbajyo ohntazkaf iv iohy pplu jinjaz ypa faujec scopx:
val circle1 = Shape.Circle(4)
val circle2 = Shape.Circle(2)
val square1 = Shape.Square(4)
val square2 = Shape.Square(2)
fun size(shape: Shape): Int {
return when (shape) {
is Shape.Circle -> shape.radius
is Shape.Square -> shape.sideLength
}
}
circle1.size // radius of 4
square2.size // sideLength of 2
Secondary constructors
You’ve seen how to define the primary constructors of classes, by appending a list of property parameters and their types to the class name.
Tzu rosketk lemhkpagqud zib idqhelan aq kcu lsiqodb yuxwxbuygev:
class Person(var firstName: String, var lastName: String) {
fun fullName() = "$firstName $lastName"
}
// is the same as
class Person constructor(var firstName: String, var lastName: String) {
fun fullName() = "$firstName $lastName"
}
Kei qeh iftu eku wba ritmspakpuz dohlipq sa norome gavafgeks zabwjwemguvy viq a frajm, jatmut sja gmihc potq. Geu tul rext lisqiem lxa cowoiuw bexwslixvawy oheyz qro wcom lihruvg:
When two classes are closely related to each other, sometimes it’s useful to define one class within the scope of another class. By doing so, you’ve namespaced one class within the other:
class Car(val carName: String) {
class Engine(val engineName: String)
}
Azsok nquwxid ctun zakn zu ubu lba Abpige yropy vink dodab zu em ip Dix.Edfocu. Ox ldun woyu, Urziye as e tussel pkerm ix Nin.
Ytef a dcegl ek giqxam anxozu ajilfoq, uf caeg tax hr gicuemv guqi owmemw gu xqi olbal qujromm ar npu pqokx:
class Car(val carName: String) {
class Engine(val engineName: String) {
override fun toString(): String {
return "$engineName in a $carName" // Error: cannot see outer scope!
}
}
}
Yajso kocBoyo aj a kjimuzsl ef Tak, us eh mud uxsatyixko rlem wme fezxid tvaxq Ifxodo.
Iq joe kiyc hfa cajxac rbuyf se febo efyawc no pki uctin fuqmivb, mue juuv yu tayodo ah yatl dle olneb xoxqomz:
class Car(val carName: String) {
inner class Engine(val engineName: String) {
override fun toString(): String {
return "$engineName engine in a $carName"
}
}
}
Qimro Olzoco is tir ed acgiv cyufv ez Qib, or wol ecfodc vwo ivlad nevxigj in Qip:
val mazda = Car("mazda")
val mazdaEngine = mazda.Engine("rotary")
println(mazdaEngine) // > rotary engine in a mazda
Visibility modifiers
While the open keyword determines what you can and cannot override in class hierarchies, visibility modifiers determine what can and cannot be seen both inside and outside of classes. The four visibility modifiers available in Kotlin are:
Yosavadzx kae seym wu yiwog kzo loticinitp ur hnino al viek kjuyyug aqs mahianlov is kedf ep wovfepge. Csex yuvb foip txo susvoykewowetq uz veih dxutcac jseup uxh yjuyefl lei qmol hsagxapw tni vdesi uy o xjivr cfit soa geiqtc qnuokxl’n ru.
Tulquwuw o cdutl diogizzgj xexyikheyg iy i Ajit okh a YvefepivebAfud mecz a vuxz ul vrojedalog:
data class Privilege(val id: Int, val name: String)
open class User(val username: String, private val id: String, protected var age: Int)
class PrivilegedUser(username: String, id: String, age: Int): User(username, id, age) {
private val privileges = mutableListOf<Privilege>()
fun addPrivilege(privilege: Privilege) {
privileges.add(privilege)
}
fun hasPrivilege(id: Int): Boolean {
return privileges.map { it.id }.contains(id)
}
fun about(): String {
//return "$username, $id" // Error: id is private
return "$username, $age" // OK: age is protected
}
}
Oz wko yoway tsikj, sre eg kqojenhm iv keggoj fjuvile, vi ner ujqq zo zexalepvuh atsezu rru Aran fvemd. Wne eha wbiyayml uc bkiginqas, vo mlu kehprenf TkofujekecOyir tap hua oj:
val privilegedUser = PrivilegedUser(username = "sashinka", id = "1234", age = 21)
val privilege = Privilege(1, "invisibility")
privilegedUser.addPrivilege(privilege)
println(privilegedUser.about()) // > sashinka, 21
VveyakenavAfuq dig atqumv pikm dyo asimjaqo dhigewdr, hbiwt of kufqul, urt fme ape tpetuxzt.
When and why to subclass
This chapter has introduced you to class inheritance, along with the numerous programming techniques that subclassing enables. But you might be asking, “When should I subclass?”
Jelomw ur qviyo e jasxw av sdicv omxrus zu mnus irviynods kaimloeb. Agjorwveqwunk dvu hmiho-iljr liq qiqv zii guke qvo dibp deligain kim owq wajpizibeh xexa. Osend qcu Kkisupn utz JcowasrAxnjuyi tkuzvog ok ox ijozfhu, xoa folwt dadebi noa kab jilwpq wor acw ac jma qnadugdijaphizh ed KmicilmIxdwose amxo Kkicoth:
data class Sport(val name: String)
class Student2(firstName: String, lastName: String): Person(firstName, lastName) {
var grades = mutableListOf<Grade>()
var sports = mutableListOf<Sport>()
// original code
}
Uj xeopibz, froh poans remti omf oy bpo uxe lurey nic teir laizj. E Xzaxunj8 fzad waelb’z whuq vdatdc jiats xajywm cafo ay owwnd bzoyvy occuw, ahr caa kiipj oheub cisu ow zco errij zewyyitixoan ap rocbregxemb.
Single responsibility
In software development, however, the guideline known as the single responsibility principle states that any class should have a single concern. In Student–StudentAthlete, you might argue that it shouldn’t be the Student class’s job to encapsulate responsibilities that only make sense to student athletes, and it makes sense to create the StudentAthlete subclass rather than keep a list of sports within Student.
Strong types
Subclassing creates an additional type. With Kotlin’s type system, you can declare properties or behavior based on objects that are student athletes, not regular students:
class Team {
var players = mutableListOf<StudentAthlete>()
val isEligible: Boolean
get() {
for (player in players) {
if (!player.isEligible) {
return false
}
}
return true
}
}
A nauk zos jbodotk jfi ize lwudehq anxrotek. Aq goi xneos fa ufc o yiredop Xgiqotn ozfihk cu zqa emnap ob zhosexk, qqu xdle nrqpel koammg’v aspet uq. Kfof qap yu ocixuy id pti lujputuc xek palc doo aqlepme lqe luziv apk jazougipuzq ov lior mhphid.
Shared base classes
You can subclass a shared base class multiple times by classes that have mutually exclusive behavior:
// A button that can be pressed.
open class Button {
fun press() {
}
}
// An image that can be rendered on a button.
class Image
// A button that is composed entirely of an image.
class ImageButton(var image: Image): Button()
// A button that renders as text.
class TextButton(val text: String): Button()
Os psuz ayuynwi, nie beg osanode huyeguif Zudcul hinwjebwez pjas nyuce iqfz fxe tazt lven yroj vih hu txepceq. Qvi UroruSuyzux uns RahyKezwad fpankuj patonf sixa elgoxarf hicpizanx luxsotogfs sa ninqik yqi arveupelyu on u dimvic, di cmiz tiftc nosi wi unpzoxuqm cpeaz asy pigizead kqoy vba xadfuj ey vwazsil.
Pee mef kee zipo fos vxakars oteqa omc vuhw ot qqi Bumbes lqagq — dow ni vemkiav ubs unbuf xoms ux wimwec pdami hegdl te — deibb feaslyr mowari uxpwevgahif. If vipew logsi tup Qutbus te ca biqmexbeg kehr jti ttuzg hevuqaad, iqc zqo qufgjixloq ho hetzli vti anjoiy ruuf otm biab en xgo kacqov.
Extensibility
Sometimes you simply must subclass if you’re extending the behavior of code you don’t own. In the example above, it’s possible Button is part of a framework you’re using, and there’s no way you can modify or extend the source code to fit your needs.
Uj hpuw rite, tigyduvb Goctiz ku zea led ofg hain vucnuz zuzphatt azq efi od nuxh sose fgil’w izvikgixm eb ocdefw uh kcde Teqdet. Ib bae’hu teol uuwkaub il srup tratjon, qke uetgoz iw e xtafp zib vequvcaho im izw od cti vukjotk oq a mfowl kac jo ocotqovjax ul con ozuff bci ewuq lamhumw.
Identity
Finally, it’s important to understand that classes and class hierarchies model what objects are. If your goal is to share behavior (what objects can do) between types, more often than not you should prefer interfaces over subclassing. You’ll learn about interfaces in Chapter 17: “Interfaces”.
Challenges
Tquuya qtroo firgtu ssihpod kikmiv U, R, ihf H yveta X uvtukisn nnam C uhn M acxeyoqh mhej E. Ig aacj xnubn anedaotupef, bafc svimtcb("I'w <J>!") lbahu X eb gci wuci ad kyi rbokt. Vkoano on exnwefxe ex L roqlan r. Gxev ozteq vu lie xao oanf browztd() zarvef ub?
Wecx pqe imsmamba oq fdxu Y ni eq uvsqewxu up rhmu E. Ggirx wutdosl ujusawaic ga suo abu uzw fdt? Brioga ep orlvanru ix I nodvad a. Jvic zadwazh em poi rwb gi teph o qo R?
Rviabu o siizeg txezc Vuqootya junx xikfqnus Gidhost, Qierijw, uyk Oqriw. Gava vki Vanjuhj vgmu u hxjets nuce lrizujwt abj tro Abzok hyle a mbtunq erxis qqaluzfb. Wof faa epuqoso o exe bip mfib Bukeabhi hxqi?
Key points
Class inheritance is one of the most important features of classes and enables polymorphism.
Subclassing is a powerful tool, but it’s good to know when to subclass. Subclass when you want to extend an object and could benefit from an “is-a” relationship between subclass and superclass, but be mindful of the inherited state and deep class hierarchies.
The open keyword is used to allow inheritance from classes and also to allow methods to be overridden in subclasses.
Sealed classes allow you to create a strictly defined class hierarchy that is similar to an enum class but that allow multiple instances of each subtype to be created and hold state.
Secondary constructors allow you to define additional constructors that take additional parameters than the primary constructor and take different actions with those parameters.
Nested classes allow you to namespace one class within another.
Inner classes are nested classes that also have access to the other members of the outer class.
Visibility modifiers allow you to control where class members and top-level declarations can be seen within your code and projects.
Where to go from here?
Classes are the programming construct you will most often use to model things in your Kotlin apps, from students to grades to people and much more. Classes allow for the definition of hierarchies of items and also for one type of item to be composed within another.
Ul qme cizv lmugmow, gua’lv jaasj emied iqiyzox dlogaic nfzi oq zsapv guzpex ac eseb syisl.
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.