Instruction

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... 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.

Unlock now

Override Method

Inheritance in Kotlin lets you create new classes, or subclasses, that inherit properties and behaviors from existing classes, or superclasses. But subclasses aren’t just copycats! They can add their own unique properties and methods, making them more specialized than their superclasses.

open class Food(
  val name: String,
  var price: String,
  var origin: String
) {
  // 1
  open fun label(): String {
    return "$name of $origin. Price: $price"
  }
}

class Fruit(
  name: String,
  price: String,
  origin: String,
  val stone: Boolean = false
) : Food(name, price, origin) {
  // 2
  fun hasStone(): Boolean {
    return stone
  }

  // 3
  override fun label(): String {
    val stonedLabel = if (hasStone()) "Stoned " else ""
    return "${stonedLabel}Fruit ${super.label()}"
  }
}

fun main() {
  val tomato = Food("Tomato", "1.0", "US")
  val tomato2 = Fruit("Tomato", "1.0", "US")
  
  println(tomato.label()) // Tomato of US. Price: 1.0
  println(tomato2.label()) // Fruit Tomato of US. Price: 1.0
  
  val peach = Fruit("Peach", "2.0", "Chile", true)
  println(peach.label()) // Stoned Fruit Peach of Chile. Price: 2.0

}

Benefits

Method overriding is a powerful tool that promotes code reusability, flexibility, and polymorphism, leading to well-structured and maintainable code.

open class Fruit {
  open fun describeColor() {
    println("Fruits can be of various colors.")
  }
}

class Peach : Fruit() {
  override fun describeColor() {
    println("A peach is usually pinkish or yellowish.")
  }
}

class Plum : Fruit() {
  override fun describeColor() {
    println("A plum is usually purple or reddish.")
  }
}

fun main() {
  val myFruit: Fruit = Fruit()
  val myPeach: Fruit = Peach()
  val myPlum: Fruit = Plum()

  myFruit.describeColor() // Fruits can be of various colors.
  myPeach.describeColor() // A peach is usually pinkish or yellowish.
  myPlum.describeColor() // A plum is usually purple or reddish.
}
fun describeFruitColor(fruit: Fruit) {
  fruit.describeColor()
}
  describeFruitColor(myPeach) // A peach is usually pinkish or yellowish.
  describeFruitColor(myPlum) // A plum is usually purple or reddish.

open class Tree {
  open fun grow() {
    println("The tree is growing.")
  }
}

class PeachTree : Tree() {
  override fun grow() {
    super.grow()
    println("The peach tree is blossoming with pinkish flowers.")
  }
}

class PlumTree : Tree() {
  override fun grow() {
    super.grow()
    println("The plum tree is blossoming with white flowers.")
  }
}

fun main() {
  val myTree: Tree = Tree()
  val myPeachTree: Tree = PeachTree()
  val myPlumTree: Tree = PlumTree()

  myTree.grow() // The tree is growing.
  myPeachTree.grow() // The tree is growing.
  //         The peach tree is blossoming with pinkish flowers.
  myPlumTree.grow() // The tree is growing.
  //         The plum tree is blossoming with white flowers.
}

Override Constructor Properties

As you already know, overriding is a very powerful tool. You can use it not only for methods but also for overriding properties.

open class Food(open val name: String)

class LocalFood(name: String, val isLocal: Boolean): Food(name) {
  // Override the name property during initialization
  override val name = name.toUpperCase()
}

class LocalFood(nameParam: String, val isLocal: Boolean): Food(nameParam) {
// Override the name property during initialization
  override val name = nameParam.toUpperCase()
  
  fun secretName(): String {
    return super.name
  }
}
fun main() {
    val food = LocalFood("Plum", false)
    println(food.name) // PLUM
    println(food.secretName()) // Plum
}

Override Special Methods

Now, it’s time to discuss two methods responsible for ensuring equality between class instances: equals() and hashCode().

equals()

So, why is equals() important?

// Overriding equals
open class Food(
  val name: String,
  var price: String,
) {

  override fun equals(other: Any?): Boolean {
    if (this === other) return true
    if (other !is Food) return false
    //1
    return name == other.name
  }
}

class Fruit(
  name: String,
  price: String,
  val stone: Boolean = false
) : Food(name, price) {
  
  fun hasStone(): Boolean {
    return stone
  }

  override fun equals(other: Any?): Boolean {
    if (this === other) return true
    if (other !is Fruit) return false
    // 2
    return name == other.name && stone == other.stone
  }
}

fun main() {
  val tomato = Food("Tomato", "1.0", "US")
  val stonedtomato = Fruit("Tomato", "1.0", "US", true)

  // 1
  println(tomato.equals(stonedtomato)) // true
  // 2
  println(stonedtomato.equals(tomato)) // false
}

hashCode()

Hashcode is mostly useful for Hash-Based Collections:

  override fun hashCode(): Int {
    return 1 + price.hashCode()
  }
fun main() {
  val tomato = Food("Tomato", "1.0")
  val cucumber = Food("Tomato", "2.0")
  //1
  val foods = mutableSetOf<Food>()
  foods.add(tomato)
  //2
  println(cucumber in foods) // false
  //3
  println(cucumber == foods.first()) // true
  //4
  println(tomato == cucumber) // true
}
See forum comments
Download course materials from Github
Previous: Introduction Next: Demo