Kotlin Inheritance and Polymorphism: open/override, Abstract Classes & Interfaces
1. Inheritance
What is inheritance in Kotlin?
Inheritance allows a class (derived or subclass) to inherit properties and methods from another class (base or superclass), promoting code reuse and hierarchy. Kotlin supports single inheritance for classes (one superclass) but multiple inheritance via interfaces. Use the : operator to specify the superclass.
Syntax:
open class BaseClass { /* properties/methods */ }
class DerivedClass : BaseClass() { /* additional members */ }
Types of Inheritance:
- Single Inheritance: A class inherits from one superclass.
- Multilevel Inheritance: A class inherits from a derived class, forming a chain.
- Hierarchical Inheritance: Multiple classes inherit from one superclass.
- Multiple Inheritance (via Interfaces): A class implements multiple interfaces.
- Note: Kotlin does not support multiple class inheritance to avoid the diamond problem.
How to Implement: Use the : operator after the class name, calling the superclass constructor. Mark superclasses with open (classes are final by default).
open class Animal(val name: String) {
fun eat() = println("$name is eating.")
}
class Dog(name: String) : Animal(name) {
fun bark() = println("$name is barking.")
}
Example:
// Inheritance example
open class Vehicle(val brand: String) {
open fun start() = println("$brand vehicle starting...")
}
class Car(brand: String) : Vehicle(brand) {
override fun start() = println("$brand car engine roaring!")
}
class Bike(brand: String) : Vehicle(brand) {
override fun start() = println("$brand bike humming!")
}
fun main() {
val car = Car("Toyota")
val bike = Bike("Honda")
car.start()
bike.start()
}
Output:
Toyota car engine roaring!
Honda bike humming!
Use in Android/Kotlin Development:
- Code Reuse: Base classes for common UI logic (e.g., Activity subclassing).
- Hierarchy: Model relationships (e.g., Vehicle base for Car and Bike).
- Extensibility: Add features to subclasses without modifying base classes.
2. Polymorphism
What is polymorphism in Kotlin?
Polymorphism allows objects of different classes to be treated as instances of a common superclass, enabling flexible code. Kotlin supports:
- Runtime Polymorphism: Method overriding with
open/override. - Compile-Time Polymorphism: Function overloading (multiple functions with same name but different parameters).
Virtual Methods: Methods in a superclass marked with open, allowing subclasses to override them. The override keyword is required in the subclass.
open class Base {
open fun method() { /* implementation */ }
}
class Derived : Base() {
override fun method() { /* new implementation */ }
}
Example:
// Polymorphism example
open class Shape {
open fun area(): Double = 0.0
open fun describe() = "A shape"
}
class Circle(val radius: Double) : Shape() {
override fun area() = Math.PI * radius * radius
override fun describe() = "Circle with radius $radius"
}
class Rectangle(val width: Double, val height: Double) : Shape() {
override fun area() = width * height
override fun describe() = "Rectangle with width $width, height $height"
}
fun printShape(shape: Shape) {
println("${shape.describe()}: Area = ${shape.area()}")
}
fun main() {
val shapes = listOf(Circle(5.0), Rectangle(4.0, 6.0))
for (shape in shapes) {
printShape(shape)
}
}
Output:
Circle with radius 5.0: Area = 78.53981633974483
Rectangle with width 4.0, height 6.0: Area = 24.0
Use in Kotlin Development:
- Flexible Algorithms: Use base interfaces for different implementations (e.g., Shape for various shapes).
- Generic Operations: Polymorphic methods for UI components or services.
- Extensibility: Add new types without modifying existing code.
3. Abstract Classes and Interfaces
What is an abstract class in Kotlin?
An abstract class cannot be instantiated and is used as a base for subclasses. It may contain abstract methods (no implementation) and concrete methods.
abstract class Base {
abstract fun abstractMethod()
fun concreteMethod() = println("Concrete")
}
What is an interface in Kotlin?
An interface defines a contract of methods and properties that implementing classes must provide. Kotlin interfaces can have default implementations.
interface Interface {
fun method() // Abstract
fun defaultMethod() = println("Default") // Default implementation
}
Example:
// Abstract class and interface example
abstract class Animal {
abstract fun makeSound()
fun sleep() = println("Sleeping")
}
interface Flyable {
fun fly()
fun land() = println("Landing") // Default implementation
}
class Bird : Animal(), Flyable {
override fun makeSound() = println("Chirp!")
override fun fly() = println("Flying")
}
fun main() {
val bird = Bird()
bird.makeSound()
bird.sleep()
bird.fly()
bird.land()
}
Output:
Chirp!
Sleeping
Flying
Landing
Use in Kotlin Development:
- Abstract Classes: Shared state/behavior in hierarchies (e.g., base Activity).
- Interfaces: Contract-based design (e.g., Repository for data access).
- Multiple Interfaces: Combine behaviors (e.g., Serializable, Comparable).
4. Common Mistakes and Best Practices
Common Mistakes:
- Inheritance:
- Forgetting
openon superclasses, preventing overriding. - Not calling
super()in constructors, missing parent initialization.
- Forgetting
- Polymorphism:
- Overriding methods without
overridekeyword, causing errors. - Not using virtual/
openfor runtime polymorphism.
- Overriding methods without
- Abstract Classes/Interfaces:
- Instantiating abstract classes.
- Not implementing all interface methods.
- General:
- Deep inheritance hierarchies, reducing maintainability.
- Ignoring null safety in polymorphic code.
Best Practices:
- Inheritance:
- Use
openfor inheritable classes; prefer interfaces for contracts. - Call
super()in constructors for proper initialization.
- Use
- Polymorphism:
- Use
overrideexplicitly; design for interface-based polymorphism. - Test overridden methods for correct behavior.
- Use
- Abstract Classes/Interfaces:
- Use abstract classes for shared state; interfaces for behavior.
- Implement default methods in interfaces for extensibility.
- General:
- Favor composition over inheritance.
- Use data classes for simple DTOs.
- Document with KDoc for classes/methods.
- Test with edge cases (e.g., null inputs).