Kotlin Open Classes & Methods | Android Introduction #21
In this reading, you will learn how to explain parent-child or subclass/superclass relationship, how child can be used where parent is expected and how to call parent constructors.
Parent-Child Relationship
When one class inherits from another, it takes all the previous class’s behavior (its methods and properties). It also becomes its subclass, which means that it can be used wherever its parent is expected. So, in the example below, Dog is a subclass of Mammal, or Mammal is a superclass of Dog. It is also called a parent-child relationship, so Dog is a child of Mammal, and Mammal is a parent of Dog.
open class Mammal {
fun feedChildren() {}
}
class Dog: Mammal {
fun fetchStick() {}
}
fun feed(mammal: Mammal) {
mammal.feedChildren()
}
fun main() {
val dog = Dog()
dog.feedChildren()
dog.fetchStick()
feed(dog)
}
Calling Superclass Constructor
When you create an instance of a class, you need to call its constructor. When a class Dog has a constructor that expects name, when you create an instance of this class, you need to specify a value for this parameter.
class Dog(val name: String)
fun main() {
val dog = Dog("Rex") // some string must be here
println(dog.name) // Rex
}
When a class is inherited from another class it takes all the behavior of the parent class. That includes its constructor properties, but to initialize them with some values, the parents’ constructor needs to be called. That means, when you call a constructor of a subclass, it also needs to call its parent constructor. How is this done? When you specify a class that you inherit from, you also need to call the constructor of this class.
open class Dog(val breed: String)
class Labrador(val name: String) : Dog("Labrador Retriever") // Here we call animals' constructor
fun main() {
val lab = Labrador("Coco")
println(lab.name) // Coco
println(lab.breed) // Labrador Retriever
}
When calling the parent constructor, you can use constructor parameters. What is important, they do not need either val or var for that.
Let’s consider the following case: Assume that our Dog class has a constructor property name. Dog has a subclass Labrador.name might be different for each dog, so it should be specified when a constructor is called. How can you do that?
One way is that inside the Labrador constructor, you define a val property name, however since such a property is already defined in the parent:
1. The property name inside Dog must be open (must have open modifier)
2. You must add override in front of the property definition in Labrador
open class Dog(open val name: String)
class Labrador(override val name: String) : Dog(name)
fun main() {
val lab = Labrador("Coco")
println(lab.name) // Coco
}
However, there is also a much easier option. Inside Labrador you can define name without val. In such a case, Labrador does not define name property, but it does not need to — it is already defined by its parent. The name parameter can be passed to the superclass constructor, and both classes behave practically the same as in the previous example.
open class Dog(val name: String)
class Labrador(name: String) : Dog(name)
fun main() {
val lab = Labrador("Coco")
println(lab.name) // Coco
}