What an abstract class is
An abstract class is a class that is meant to be extended, never created directly. It describes what every member of a family of classes has in common and leaves some pieces open for the subclasses to fill in. Those open pieces are abstract members: they are declared with a signature but no body.
In Kotlin you mark both the class and the open member with the abstract keyword:
abstract class Animal {
abstract fun speak()
}
Every animal can speak, but how it speaks depends on the kind of animal. Animal states the promise (speak() exists) and each concrete subclass decides what it does. The abstract class acts as a contract that the compiler enforces.
The two rules the compiler enforces
You can't create an instance of an abstract class
Kotlin has no new keyword. Calling the class name, as in Animal(), creates an object, and for an abstract class the compiler refuses:
abstract class Animal { abstract fun speak() }
fun main() {
val x = Animal()
}
The error is cannot create an instance of an abstract class. To get an object you need a concrete subclass.
A concrete subclass must implement every abstract member
A class that extends Animal and does not provide speak() is itself incomplete. The compiler reports class 'Cat' is not abstract and does not implement abstract base class member, followed by the missing function. The only way around it is to make the subclass abstract too and leave the work to its own subclasses.
Step by step: the exercise
The lesson gives you the abstract Animal class and a Solution class whose mainFunction creates a Dog and calls speak() on it. The program must print Woof, so the missing piece is the Dog class.
- Declare
Dogas a subclass ofAnimal. Writeclass Dog : Animal(). The colon means "extends". The parentheses afterAnimalare the call to the parent's constructor, and Kotlin requires them whenever the parent is a class. Without them the compiler saysthis type has a constructor, so it must be initialized here. - Override
speak. InsideDog, writeoverride fun speak()with a body that callsprintln("Woof"). Theoverridekeyword is mandatory in Kotlin. It tells the compiler, and the reader, that this function fills in a member declared by a parent.
With both steps done, mainFunction compiles. Its call rex.speak() runs the version inside Dog and prints Woof.
The solution in Kotlin
abstract class Animal {
abstract fun speak()
}
class Dog : Animal() {
override fun speak() {
println("Woof")
}
}
class Solution {
fun mainFunction() {
val rex = Dog()
rex.speak()
}
}
The Kotlin details worth knowing:
- Abstract members are open automatically. Classes and members are final by default in Kotlin, so a normal function can't be overridden unless it is marked
open. An abstract member exists only to be overridden, so you never writeopennext toabstract. overrideis required. Writingfun speak()inDogwithout it is a compile error:'speak' hides member of supertype 'Animal' and needs an 'override' modifier.- The declared type picks what you can call, the real object picks what runs. If you write
val a: Animal = Dog(), thena.speak()still printsWoof, because the call goes to the actualDogobject. - Return types.
speak()declares no return type, so it returnsUnit, Kotlin's "nothing useful" value. A subclass must keep a compatible signature.
Mixing abstract and concrete members
An abstract class may also hold ordinary members: constructor parameters, properties and functions with bodies. Subclasses inherit them as they are. Only the members marked abstract must be overridden. Properties can be abstract as well:
abstract class Shape {
abstract val sides: Int
fun label() = "shape with $sides sides"
}
class Triangle : Shape() {
override val sides = 3
}
fun main() {
println(Triangle().label())
}
This prints shape with 3 sides. The shared function label() is written once in Shape and relies on the abstract property that Triangle supplies. This pattern, a shared algorithm in the parent with the variable parts left abstract, is the main reason to choose an abstract class.
An abstract class can have a constructor with parameters too. A subclass then passes the arguments in the parent call, for example class Dog(name: String) : Animal(name) when Animal is declared as abstract class Animal(val name: String).
Abstract class or interface?
A Kotlin class can extend only one parent class, but it can implement many interfaces. Choose an abstract class when subclasses should share state or code and form a clear "is a" family. Choose an interface when you only want to describe a capability that unrelated classes can have.
Common mistakes
- Forgetting the parentheses in
: Animal(). The compiler reports that the type has a constructor and must be initialized. - Forgetting
override. The function then looks like a new member that hides the parent's, which Kotlin rejects. - Not implementing every abstract member. One missing implementation is enough to make the subclass an error.
- Trying to instantiate the abstract class to test it. Create
Dog()instead. - Printing with the wrong text. The output must be exactly
Woof, with that capital W and no extra characters.
Key takeaways
- An abstract class is declared with
abstract classand can't be instantiated. - Abstract members have no body, and every concrete subclass must implement them with
override. - Extending a class needs the parent constructor call, as in
: Animal(). - Abstract classes can also carry shared properties and functions with bodies.
- Calls on an
Animalvariable run the subclass's version.