← Back to Kotlin Course | Chapter 7: Inheritance & Interfaces | Lesson 3 of 7

Abstract Classes

An abstract class is a blueprint that is deliberately left unfinished, so it can never be built directly -- only its finished subclasses can.

Declaring an Abstract Class

abstract class marks a class that cannot be instantiated on its own -- it exists only to be subclassed, often to define a shared shape for related types.

Example: Declaring an Abstract Class

markup
abstract class Shape {
    abstract fun area(): Double
}

class Circle(val radius: Double) : Shape() {
    override fun area(): Double = Math.PI * radius * radius
}

fun main() {
    val circle = Circle(3.0)
    println("Area: ${circle.area()}")
}

Abstract Members Have No Body

An abstract fun or abstract val declares only a signature, with no implementation -- every concrete subclass is required to provide one using override.

Example: Abstract Members Have No Body

markup
abstract class Employee(val name: String) {
    abstract fun monthlySalary(): Double
}

class Manager(name: String, val baseSalary: Double) : Employee(name) {
    override fun monthlySalary(): Double = baseSalary * 1.2
}

fun main() {
    val manager = Manager("Ana", 5000.0)
    println("${manager.name}'s salary: ${manager.monthlySalary()}")
}

Mixing Abstract and Concrete Members

An abstract class can define fully implemented members alongside abstract ones, sharing common behavior while still leaving specific details to subclasses.

Example: Mixing Abstract and Concrete Members

markup
abstract class Shape {
    abstract fun area(): Double

    fun describe(): String = "This shape has area ${area()}"
}

class Square(val side: Double) : Shape() {
    override fun area(): Double = side * side
}

fun main() {
    val square = Square(5.0)
    println(square.describe())
}

Abstract Classes vs Regular Open Classes

Use an abstract class when there is no sensible default implementation and instantiating the base type directly would not make sense; use a regular open class when a default, usable implementation exists.

Example: Abstract Classes vs Regular Open Classes

markup
abstract class PaymentMethod {
    abstract fun pay(amount: Double): String
}

class CreditCard : PaymentMethod() {
    override fun pay(amount: Double): String = "Paid $$amount with credit card"
}

fun main() {
    val payment: PaymentMethod = CreditCard()
    println(payment.pay(49.99))
}
Common Mistakes
  1. Trying to instantiate an abstract class directly with AbstractClass(), which the compiler always rejects.
  2. Forgetting that abstract members are implicitly open and don't need an extra open keyword.
  3. Not implementing every abstract member in a concrete subclass, leaving the subclass abstract too without realizing it.
Chapter Summary
  • abstract class cannot be instantiated directly; only its concrete (non-abstract) subclasses can be.
  • abstract fun/abstract val declare members with no implementation that subclasses must provide.
  • Abstract members are implicitly open, so a subclass overriding them just needs override, not open too.
  • An abstract class can still have concrete (fully implemented) members alongside its abstract ones.
🔒

Chapter Quiz — Complete all 7 topics to unlock

0/7 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.