Introduction

In this section, we will explore two fundamental concepts of Object-Oriented Programming (OOP) in Kotlin: inheritance and interfaces. These concepts allow you to create more modular, reusable, and maintainable code.

Inheritance

What is Inheritance?

Inheritance is a mechanism in OOP that allows a new class (derived class) to inherit properties and methods from an existing class (base class). This promotes code reuse and establishes a natural hierarchy between classes.

Syntax

In Kotlin, inheritance is declared using the open keyword for the base class and the : symbol for the derived class.

open class Animal {
    fun eat() {
        println("Eating...")
    }
}

class Dog : Animal() {
    fun bark() {
        println("Barking...")
    }
}

fun main() {
    val dog = Dog()
    dog.eat()  // Inherited method
    dog.bark() // Own method
}

Explanation

  • open class Animal: The open keyword allows the class to be inherited.
  • class Dog : Animal(): The Dog class inherits from the Animal class.
  • dog.eat(): The Dog class can use the eat method from the Animal class.

Practical Example

Let's create a more detailed example with a base class Vehicle and a derived class Car.

open class Vehicle(val name: String, val speed: Int) {
    open fun displayInfo() {
        println("Vehicle Name: $name, Speed: $speed")
    }
}

class Car(name: String, speed: Int, val fuelType: String) : Vehicle(name, speed) {
    override fun displayInfo() {
        super.displayInfo()
        println("Fuel Type: $fuelType")
    }
}

fun main() {
    val car = Car("Toyota", 120, "Petrol")
    car.displayInfo()
}

Explanation

  • open fun displayInfo(): The open keyword allows the method to be overridden.
  • override fun displayInfo(): The Car class overrides the displayInfo method to add more information.

Interfaces

What is an Interface?

An interface in Kotlin is a blueprint of a class that contains abstract methods and properties. A class can implement multiple interfaces, providing a way to achieve multiple inheritance.

Syntax

Interfaces are declared using the interface keyword.

interface Drivable {
    fun drive()
}

class Bicycle : Drivable {
    override fun drive() {
        println("Driving a bicycle")
    }
}

fun main() {
    val bicycle = Bicycle()
    bicycle.drive()
}

Explanation

  • interface Drivable: Declares an interface with an abstract method drive.
  • class Bicycle : Drivable: The Bicycle class implements the Drivable interface.
  • override fun drive(): The Bicycle class provides an implementation for the drive method.

Practical Example

Let's create an interface Flyable and a class Airplane that implements it.

interface Flyable {
    fun fly()
}

class Airplane(val model: String) : Flyable {
    override fun fly() {
        println("$model is flying")
    }
}

fun main() {
    val airplane = Airplane("Boeing 747")
    airplane.fly()
}

Explanation

  • interface Flyable: Declares an interface with an abstract method fly.
  • class Airplane : Flyable: The Airplane class implements the Flyable interface.
  • override fun fly(): The Airplane class provides an implementation for the fly method.

Combining Inheritance and Interfaces

Example

Let's create a class FlyingCar that inherits from Vehicle and implements Flyable.

interface Flyable {
    fun fly()
}

open class Vehicle(val name: String, val speed: Int) {
    open fun displayInfo() {
        println("Vehicle Name: $name, Speed: $speed")
    }
}

class FlyingCar(name: String, speed: Int, val fuelType: String) : Vehicle(name, speed), Flyable {
    override fun displayInfo() {
        super.displayInfo()
        println("Fuel Type: $fuelType")
    }

    override fun fly() {
        println("$name is flying at speed $speed")
    }
}

fun main() {
    val flyingCar = FlyingCar("SkyCar", 200, "Electric")
    flyingCar.displayInfo()
    flyingCar.fly()
}

Explanation

  • class FlyingCar : Vehicle, Flyable: The FlyingCar class inherits from Vehicle and implements Flyable.
  • override fun fly(): The FlyingCar class provides an implementation for the fly method.

Exercises

Exercise 1

Create a base class Person with properties name and age, and a method displayInfo. Then, create a derived class Student that adds a property studentId and overrides the displayInfo method to include the studentId.

Solution

open class Person(val name: String, val age: Int) {
    open fun displayInfo() {
        println("Name: $name, Age: $age")
    }
}

class Student(name: String, age: Int, val studentId: String) : Person(name, age) {
    override fun displayInfo() {
        super.displayInfo()
        println("Student ID: $studentId")
    }
}

fun main() {
    val student = Student("Alice", 20, "S12345")
    student.displayInfo()
}

Exercise 2

Create an interface Playable with a method play. Then, create a class Guitar that implements Playable and provides an implementation for the play method.

Solution

interface Playable {
    fun play()
}

class Guitar : Playable {
    override fun play() {
        println("Playing the guitar")
    }
}

fun main() {
    val guitar = Guitar()
    guitar.play()
}

Conclusion

In this section, we covered the concepts of inheritance and interfaces in Kotlin. We learned how to create base and derived classes, override methods, and implement interfaces. These concepts are fundamental for building modular and reusable code in Kotlin. In the next section, we will explore visibility modifiers to control access to class members.

© Copyright 2024. All rights reserved