Dynamic Dispatch
Part of the Object Oriented Programming section of Coddy's Kotlin journey. Lesson 27 of 57.
Polymorphism means that one call can behave in many ways. When you call an overridden method through a superclass or interface type, Kotlin looks at the object's actual class while the program runs and executes that class's version. This is dynamic dispatch:
abstract class Animal(val name: String) {
abstract fun sound(): String
fun speak() = "$name: ${sound()}"
}
class Dog(name: String) : Animal(name) {
override fun sound() = "Woof"
}
class Cow(name: String) : Animal(name) {
override fun sound() = "Moo"
}Inside main:
val barn: List<Animal> = listOf(Dog("Rex"), Cow("Bess"), Dog("Fido"))
for (a in barn) println(a.speak())Output:
Rex: Woof
Bess: Moo
Fido: WoofThe calling code does not change when a new subclass appears. totalArea below was written for any Shape, so it works with Triangle too, although nobody changed it:
interface Shape {
fun area(): Double
}
class Square(val side: Double) : Shape {
override fun area() = side * side
}
class Triangle(val base: Double, val height: Double) : Shape {
override fun area() = base * height / 2
}
fun totalArea(shapes: List<Shape>) = shapes.sumOf { it.area() }Inside main:
println(totalArea(listOf(Square(2.0), Triangle(3.0, 4.0))))Output:
10.0Dispatch looks at the object, not at the variable. Assigning another object to the same variable changes which code runs on the next call:
interface Mode {
fun format(text: String): String
}
class Normal : Mode {
override fun format(text: String) = text
}
class Shout : Mode {
override fun format(text: String) = text.uppercase() + "!"
}Inside main:
var mode: Mode = Normal()
println(mode.format("hello"))
mode = Shout()
println(mode.format("hello"))Output:
hello
HELLO!Polymorphism replaces checks like if (kind == "dog") that would otherwise be repeated wherever animals are used. Each class keeps its own behaviour, and adding a kind of animal means adding one class:
// without polymorphism: every function checks the kind
fun sound(kind: String) = when (kind) { "dog" -> "Woof"; "cow" -> "Moo"; else -> "?" }
// with polymorphism: the new kind brings its own behaviour
class Sheep(name: String) : Animal(name) {
override fun sound() = "Baa"
}Challenge
EasyCharacter(name, hp) is an open class. takeHit(damage) subtracts the damage from hp (never below 0), and status() returns Ada: 30 hp or Ada: down at 0. hp can be read everywhere but only changed inside Character. Complete the subclasses by overriding takeHit and passing the changed damage on to super.takeHit: Warrior (40 hp) takes half the damage, rounded down; Mage (25 hp) has a shield that absorbs the whole first hit; Rogue (30 hp) dodges every second hit it receives (the 2nd, 4th, ...).
The supplied code reads the party as warrior:Ada,mage:Bo,rogue:Cy and then hits all 10 or Ada 12. It calls takeHit through the type Character and finally prints every status().
Your code goes in Character.kt, Warrior.kt, Mage.kt and Rogue.kt. Main.kt holds the supplied input/output code and cannot be edited.
Try it yourself
fun main() {
// Supplied input/output code: keep it as it is
val input = generateSequence(::readLine).toList()
val party = input[0].split(",").map { spec ->
val (kind, name) = spec.split(":")
when (kind) {
"warrior" -> Warrior(name)
"mage" -> Mage(name)
else -> Rogue(name)
}
}
for (cmd in input.drop(1)) {
val (target, damage) = cmd.split(" ")
for (c in party) {
if (target == "all" || target == c.name) c.takeHit(damage.toInt())
}
}
for (c in party) println(c.status())
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Classes and Objects
Working With FilesDefining ClassesPrimary Constructorsinit Blocks and ValidationSecondary ConstructorsRecap - Library Card4Inheritance
Open Classes and SubclassesOverriding Memberssuper and ConstructorsAbstract ClassesRecap - Staff Directory2Properties and Encapsulation
Custom AccessorsVisibility ModifiersPrivate Setterslazy and lateinitRecap - Bank Account5Interfaces
Declaring InterfacesDefault MethodsMultiple InterfacesInterfaces as TypesRecap - Payment Methods8Generics
Generic FunctionsGeneric ClassesType ConstraintsVariance With in and outRecap - Priority Queue11Project: Library Management
Books and MembersBorrowing Books3Objects and Companions
Object DeclarationsCompanion ObjectsConstants With constObject ExpressionsRecap - Ticket Office6Polymorphism
Dynamic DispatchType Checks and Smart Castswhen With TypesExtension FunctionsRecap - Zoo KeeperPractice on your own: Kotlin playground