Kotlin Programming 2020 Fall - Lecture 10 === ###### tags: `Kotlin` This lecture intends to cover the materials in Chapter 16. ## Interface + Classes share common properties and functions: they all implement an interface. + Interface can specify a set of classes. + Interface has no constructor. + The classes implementing the same interface may still have much different nature. + We cannot generate a pure instance of any interface. + You may define a default implementation for a property or a function. ```kotlin= enum class DamageStatus { DESTROYED, SEVERE, MEDIUM, SLIGHT, UNDAMAGED } interface Destructible { val damage: DamageStatus val destroyed: Boolean get() = damage == DamageStatus.DESTROYED fun restore(cost: Int) fun absorb(hit: Int) } interface Reporter { fun report() } class Destroyer(val name: String, val maxHitPoint: Int, val power: Int) : Destructible, Reporter { var hitPoint = maxHitPoint override val damage: DamageStatus get() = when (3 * hitPoint / maxHitPoint) { 3 -> DamageStatus.UNDAMAGED 2 -> DamageStatus.SLIGHT 1 -> DamageStatus.MEDIUM else -> { if (hitPoint == 0) DamageStatus.DESTROYED else DamageStatus.SEVERE } } // Pay 100 to recover 1 hit point override fun restore(cost: Int) { // there is no way to restore a destroyed ship if (destroyed) return hitPoint = minOf(maxHitPoint, hitPoint + cost / 100) } // Every 500 hit reduce 1 hit point override fun absorb(hit: Int) { val damageHP = minOf(hitPoint, hit / 500) hitPoint -= damageHP } // the "hit" generate by this destroyer val attackPoint: Int get() = when (damage) { DamageStatus.UNDAMAGED -> 1000 * power DamageStatus.SLIGHT -> 900 * power DamageStatus.MEDIUM -> 700 * power DamageStatus.SEVERE -> 100 * power DamageStatus.DESTROYED -> 0 } fun attack(obj: Destructible) { obj.absorb(attackPoint) } override fun report() { println("=======================================") println("Destroyer $name:") println("HP/MAX: $hitPoint/$maxHitPoint $damage") println("Power/Attack Point: $power/$attackPoint") } } class Bunker(val name: String) : Destructible, Reporter { var new = true override val damage: DamageStatus get() = if (!new) DamageStatus.DESTROYED else DamageStatus.UNDAMAGED // Any hit > 0 will destroy the bunker override fun absorb(hit: Int) { if (hit > 0) new = false } // Any cost > 0 will restore the bunker to undamaged override fun restore(cost: Int) { if (cost > 0) new = true } override fun report() { println("=======================================") println("$name is $damage.") } } fun main() { val hibiki = Destroyer("Hibiki", 20, 9) val ikazuchi = Destroyer("Ikazuchi", 15, 8) hibiki.report() ikazuchi.report() println("=======================================") println("Ikazuchi attacks Hibiki") ikazuchi.attack(hibiki) hibiki.report() println("=======================================") println("Hibiki attacks Ikazuchi") hibiki.attack(ikazuchi) ikazuchi.report() println("=======================================") println("Restore Hibiki with 1000") hibiki.restore(1000) hibiki.report() println("=======================================") println("Hibiki attacks Ikazuchi again") hibiki.attack(ikazuchi) ikazuchi.report() println("=======================================") println("Restore Ikazuchi with 1000000000") ikazuchi.restore(1_000_000_000) ikazuchi.report() val bunkerA = Bunker("Bunker A") val bunkerB = Bunker("Bunker B") println("=======================================") println("Hibiki attacks Bunker A") hibiki.attack(bunkerA) bunkerA.report() println("=======================================") println("Ikazuchi attacks Bunker B") ikazuchi.attack(bunkerB) bunkerB.report() println("=======================================") println("restore Bunker A with 1") bunkerA.restore(1) bunkerA.report() println("=======================================") println("The following are destroyed.") listOf(bunkerA, bunkerB, hibiki, ikazuchi) .filter { (it as Destructible).destroyed } .forEach { (it as Reporter).report() } } ``` ## Abstract class + An abstract class may have abstract properties and abstract functions that are not implemented in the definition of the abstract class. + Abstract properties / functions may not have implementation in the abstract classes, so they must be implemented in the subclasses. + We may use abstract class to define the common properties / functions of its subclass. + This may reduce repeatition of codes in subclasses. + But we must implement all abstract properties / functions in every non-abstract subclass. + Like interface, abstract classes may not create instances, since there may have some properties / functions not implemented yet. + Despite every abstract class has construcotrs, but these constructors are called by its subclasses and used for initialization of common parts among subclasses. + Why don't we just define the properties / functions in all subclasses instead of using abstract properties / functions? + We will need specific codes to deal with a specific subclass. And the codes may repeat a lot. + We want more flexibility. + Why not interface? Abstract classes may have back fields for properties. Note that a property of an interface may not use `field` -- Kotlin does not allow an interface to have back fields. ```kotlin= enum class DamageStatus { DESTROYED, SEVERE, MEDIUM, SLIGHT, UNDAMAGED } interface Destructible { val damage: DamageStatus val destroyed: Boolean get() = damage == DamageStatus.DESTROYED fun restore(cost: Int) fun absorb(hit: Int) } interface Reporter { fun report() } abstract class Ship(val name: String, val maxHitPoint: Int) : Destructible, Reporter { var hitPoint = maxHitPoint abstract val type: String override val damage: DamageStatus get() = when (3 * hitPoint / maxHitPoint) { 3 -> DamageStatus.UNDAMAGED 2 -> DamageStatus.SLIGHT 1 -> DamageStatus.MEDIUM else -> { if (hitPoint == 0) DamageStatus.DESTROYED else DamageStatus.SEVERE } } // Pay 100 to recover 1 hit point override fun restore(cost: Int) { if (destroyed) { println("There is no way to repair a destroyed ship.") return } hitPoint = minOf(maxHitPoint, hitPoint + cost / 100) println("$name is being repaired, and it's hit point becomes $hitPoint") } // Every 500 hit reduce 1 hit point override fun absorb(hit: Int) { val damageHP = minOf(hitPoint, hit / 500) hitPoint -= damageHP println("$name absorbs attacks and loses $damageHP hit points.") } override fun report() { println("=======================================") println("$type $name:") println("HP/MAX: $hitPoint/$maxHitPoint $damage") } abstract fun attack(obj: Destructible) abstract fun fix(obj: Destructible) } class Destroyer(name: String, maxHitPoint: Int, val power: Int) : Ship(name, maxHitPoint) { override val type = "Destroyer" val attackPoint: Int get() = when (damage) { DamageStatus.UNDAMAGED -> 1000 * power DamageStatus.SLIGHT -> 900 * power DamageStatus.MEDIUM -> 700 * power DamageStatus.SEVERE -> 100 * power DamageStatus.DESTROYED -> 0 } override fun attack(obj: Destructible) { if (destroyed) { return println("$type $name cannot attack any other.") } println("$type $name attacks!") obj.absorb(attackPoint) } override fun fix(obj: Destructible) { println("A destroyer does not fix anything.") } override fun report() { super.report() println("Power/Attack Point: $power/$attackPoint") } } class RepairShip(name: String, maxHitPoint: Int, val power: Int) : Ship(name, maxHitPoint) { override val type = "Repair Ship" override val damage: DamageStatus get() = super.damage val attackPoint: Int get() = if (damage == DamageStatus.UNDAMAGED) 500 * power else 0 val fixPoint: Int get() = when (damage) { DamageStatus.UNDAMAGED -> 200 * power DamageStatus.SLIGHT -> 100 * power DamageStatus.MEDIUM -> 50 * power DamageStatus.SEVERE -> 20 * power DamageStatus.DESTROYED -> 0 } override fun attack(obj: Destructible) { if (destroyed) { return println("$type $name cannot attack any other.") } println("$type $name attacks!") obj.absorb(attackPoint) } override fun fix(obj: Destructible) { if (destroyed) { return println("$type $name cannot fix any other.") } println("$type $name start to fixes some destructible.") obj.restore(fixPoint) } override fun report() { super.report() println("Power/Attack Point/Fix Point: $power/$attackPoint/$fixPoint") } } class Bunker(val name: String) : Destructible, Reporter { private var new = true override val damage: DamageStatus get() = if (!new) DamageStatus.DESTROYED else DamageStatus.UNDAMAGED // Any hit > 0 will destroy the bunker override fun absorb(hit: Int) { if (hit > 0) new = false } // Any cost > 0 will repair the bunker to undamaged override fun restore(cost: Int) { if (cost > 0) new = true } override fun report() { println("=======================================") println("$name is $damage.") } } fun main() { val hibiki = Destroyer("Hibiki", 20, 9) val ikazuchi = Destroyer("Ikazuchi", 15, 8) val akashi = RepairShip("Akashi", 100, 3) val ships = listOf(hibiki, ikazuchi, akashi) ships.forEach { it.report() } println("=======================================") ikazuchi.attack(hibiki) hibiki.report() println("=======================================") akashi.fix(hibiki) hibiki.report() println("=======================================") hibiki.attack(akashi) akashi.report() val bunker = Bunker("Bunker") println("=======================================") akashi.attack(bunker) bunker.report() println("=======================================") hibiki.attack(ikazuchi) ikazuchi.report() println("=======================================") println("Report the damage status of the ships:") ships.forEach { println("${it.type} ${it.name}: ${it.damage}") } } ```