Inheritance
## Learning Objectives
- Understand inheritance in Kotlin
- Work with abstract classes
- Implement interfaces
- Use interface delegation
- Override methods and properties
## Class Inheritance
### Open Classes
In Kotlin, classes are final by default. Use `open` to allow inheritance:
```kotlin
open class Animal(val name: String) {
open fun makeSound() {
println("Some sound")
}
}
class Dog(name: String) : Animal(name) {
override fun makeSound() {
println("$name says Woof!")
}
}
fun main() {
val dog = Dog("Buddy")
dog.makeSound() // Buddy says Woof!
}
```
### Superclass Constructor
```kotlin
open class Person(val name: String, var age: Int) {
open fun introduce() {
println("I'm $name, $age years old")
}
}
class Student(name: String, age: Int, val grade: String) : Person(name, age) {
override fun introduce() {
super.introduce()
println("I'm in grade $grade")
}
}
fun main() {
val student = Student("Alice", 15, "10th")
student.introduce()
}
```
## Abstract Classes
```kotlin
abstract class Shape {
abstract fun area(): Double
fun description() {
println("This shape has area: ${area()}")
}
}
class Circle(val radius: Double) : Shape() {
override fun area(): Double = Math.PI * radius * radius
}
class Rectangle(val width: Double, val height: Double) : Shape() {
override fun area(): Double = width * height
}
fun main() {
val shapes = listOf(Circle(5.0), Rectangle(4.0, 3.0))
for (shape in shapes) {
shape.description()
}
}
```
## Interfaces
### Basic Interfaces
```kotlin
interface Drawable {
fun draw()
}
class Circle(val radius: Double) : Drawable {
override fun draw() {
println("Drawing circle with radius $radius")
}
}
fun main() {
val drawable: Drawable = Circle(5.0)
drawable.draw()
}
```
### Interface with Default Implementation
```kotlin
interface Greeter {
fun greet()
fun farewell() {
println("Goodbye!")
}
}
class Person(val name: String) : Greeter {
override fun greet() {
println("Hello, I'm $name")
}
}
fun main() {
val person = Person("Alice")
person.greet()
person.farewell()
}
```
### Multiple Interfaces
```kotlin
interface Printable {
fun print()
}
interface Serializable {
fun serialize(): String
}
class Document(val content: String) : Printable, Serializable {
override fun print() {
println(content)
}
override fun serialize(): String {
return content
}
}
fun main() {
val doc = Document("Hello World")
doc.print()
println(doc.serialize())
}
```
## Interface Conflict Resolution
```kotlin
interface A {
fun foo() = "A"
fun bar()
}
interface B {
fun foo() = "B"
fun bar()
}
class C : A, B {
override fun foo() = super.foo() + super.foo()
override fun bar() {
println("bar() implementation")
}
}
fun main() {
val c = C()
println(c.foo()) // AB
c.bar()
}
```
## Delegation
### By Interface
```kotlin
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() {
println(x)
}
}
class Derived(b: Base) : Base by b
fun main() {
val b = BaseImpl(10)
Derived(b).print() // 10
}
```
### Real-world Example
```kotlin
interface Coworker {
fun work()
fun takeBreak()
}
class Manager(val name: String) : Coworker {
override fun work() {
println("$name is managing")
}
override fun takeBreak() {
println("$name takes a break")
}
}
class Assistant(manager: Manager) : Coworker by manager {
fun scheduleMeeting() {
println("Scheduling meeting")
}
}
fun main() {
val manager = Manager("Alice")
val assistant = Assistant(manager)
assistant.work() // Alice is managing
assistant.takeBreak() // Alice takes a break
assistant.scheduleMeeting()
}
```
## Overriding Members
### Override Methods
```kotlin
open class Base {
open fun method() = "Base"
}
class Derived : Base() {
override fun method() = "Derived"
}
fun main() {
val base: Base = Derived()
println(base.method()) // Derived
}
```
### Override Properties
```kotlin
open class Person {
open val name: String = "Unknown"
}
class Student : Person() {
override val name: String = "Student"
}
fun main() {
println(Student().name) // Student
}
```
### Override Rules
```kotlin
open class A {
open fun foo() = "A"
open val bar = "A-bar"
}
interface B {
fun foo() = "B" // Default implementation
val bar: String // Must be overridden
}
class C : A(), B {
override fun foo() = super.foo() + super.foo()
override val bar: String
get() = super.bar
}
fun main() {
val c = C()
println(c.foo()) // AB
println(c.bar) // A-bar
}
```
## Type Checking and Casting
```kotlin
open class Person(val name: String)
class Student(name: String, val grade: Int) : Person(name)
class Teacher(name: String, val subject: String) : Person(name)
fun main() {
val people = listOf(Person("John"), Student("Alice", 10), Teacher("Bob", "Math"))
for (person in people) {
when (person) {
is Student -> println("${person.name} is a student in grade ${person.grade}")
is Teacher -> println("${person.name} teaches ${person.subject}")
is Person -> println("${person.name} is a person")
}
}
}
```
## Summary
- Classes are final by default; use `open` to allow inheritance
- Abstract classes and methods use `abstract` keyword
- Interfaces can have default implementations
- A class can implement multiple interfaces
- Use `super` to call parent implementations
- `by` keyword delegates interface implementation
- Override methods/properties with `override` keyword
- Smart casts work after `is` checks
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →