Question

Kotlin Inheritance and Interface Implementation: Extend a Class and Implement an Interface

kotlinjava

Question

In Java, a class can extend a superclass while implementing one or more interfaces:

class MyClass extends SuperClass implements MyInterface {
    // ...
}

Can Kotlin do the equivalent? Assume that SuperClass is abstract and does not implement MyInterface.

Short Answer

Kotlin supports extending one class and implementing one or more interfaces. Instead of separate extends and implements keywords, Kotlin uses a single colon (:) followed by the superclass and interfaces. By the end, you will know the syntax, how constructors affect inheritance, and how to implement required interface members.

Concept

Kotlin supports single inheritance for classes: a class can have one direct superclass. It also supports implementing multiple interfaces.

Kotlin combines Java's extends and implements syntax into one list after a colon:

class Child : Parent(), SomeInterface, AnotherInterface
  • Parent() means Child inherits from Parent and calls its constructor.
  • SomeInterface and AnotherInterface are interfaces that Child implements.
  • If the parent class constructor needs arguments, provide them inside the parentheses.

An abstract superclass does not need to implement the interface itself. The subclass can inherit from that abstract superclass and independently implement the interface.

This matters because many designs use an abstract base class for shared state or shared behavior, then use interfaces to express capabilities such as being serializable, clickable, comparable, or able to produce a result.

Mental Model

Think of a Kotlin class as a person with one family line and several skills.

  • The superclass is the family line: the class can inherit from only one direct parent class.
  • Interfaces are skills or contracts: the class can promise to support many of them.

For example, a ReportExporter might inherit common file-handling behavior from BaseExporter while also promising to support the Exportable and Loggable contracts.

Take Quiz

Syntax and Examples

Use a colon after the class name. Put the superclass first, followed by interfaces.

abstract class SuperClass {
    abstract fun process()
}

interface MyInterface {
    fun save()
}

class MyClass : SuperClass(), MyInterface {
    override fun process() {
        println("Processing data")
    }

    override fun save() {
        println("Saving data")
    }
}

MyClass does two things:

  1. It extends SuperClass, so it must implement the abstract process() function.
  2. It implements MyInterface, so it must implement save().

If the parent class has constructor parameters, pass them when declaring the subclass:

abstract class SuperClass(val name: String)

interface MyInterface {
    fun save()
}

class MyClass(name: String) : SuperClass(name), MyInterface {
    override fun save() {
        println("Saving $name")
    }
}

Interfaces do not use constructor parentheses unless they are declared as a class-like type, which ordinary Kotlin interfaces are not.

Step by Step Execution

Consider this example:

abstract class Job {
    abstract fun run()
}

interface Loggable {
    fun log(message: String)
}

class BackupJob : Job(), Loggable {
    override fun run() {
        log("Backup started")
        println("Copying files...")
        log("Backup finished")
    }

    override fun log(message: String) {
        println("LOG: $message")
    }
}

fun main() {
    val job = BackupJob()
    job.run()
}

Execution proceeds as follows:

  1. BackupJob is declared as a subclass of Job and an implementation of Loggable.
  2. Because Job.run() is abstract, BackupJob provides override fun run().
  3. Because Loggable.log() has no implementation, BackupJob provides override fun log(...).
  4. main creates a BackupJob object.
  5. Calling job.run() prints log messages and the backup action.

Output:

LOG: Backup started
Copying files...
LOG: Backup finished

Real World Use Cases

Common uses of a superclass plus interfaces include:

  • UI components: inherit shared behavior from a base component and implement click, focus, or lifecycle interfaces.
  • API services: extend a base service that provides shared configuration while implementing an interface required by dependency injection or testing.
  • Data import jobs: inherit shared job setup and implement interfaces such as Runnable, a project-specific Validatable, or a reporting contract.
  • Domain models: extend a base entity with an ID and timestamps while implementing interfaces such as Comparable.
  • Testing: extend a shared test fixture class while implementing a test-specific contract or callback interface.

For example, Kotlin classes frequently implement Java interfaces too:

class NameComparator : Comparator<String> {
    override fun compare(a: String, b: String): Int {
        return a.compareTo(b)
    }
}
Take Quiz

Real Codebase Usage

In production Kotlin code, developers often separate shared implementation from contracts:

abstract class BaseRepository {
    protected fun requireId(id: String) {
        require(id.isNotBlank()) { "id must not be blank" }
    }
}

interface UserRepository {
    fun findName(id: String): String?
}

class InMemoryUserRepository : BaseRepository(), UserRepository {
    private val users = mapOf("u1" to "Ava")

    override fun findName(id: String): String? {
        requireId(id) // Shared base-class behavior
        return users[id] // Interface contract
    }
}

This pattern helps with:

  • Dependency inversion: application code depends on UserRepository, not the concrete implementation.
  • Testing: tests can substitute a fake implementation of an interface.
  • Shared utilities: a base class can hold protected helper functions or common state.
  • Guard clauses and validation: base classes can centralize validation, while subclasses provide specialized behavior.

Prefer an interface when you need a contract. Use an abstract base class only when subclasses genuinely need shared state or shared implementation.

Common Mistakes

Forgetting the superclass constructor call

A Kotlin class must call its superclass constructor when extending a class.

abstract class Parent

// Incorrect: Parent is a class, so its constructor call is required.
class Child : Parent, Runnable

Correct:

class Child : Parent(), Runnable {
    override fun run() {}
}

Adding parentheses to an interface

interface Printable {
    fun print()
}

// Incorrect
class Report : Printable() {
    override fun print() {}
}

Correct:

class Report : Printable {
    override fun print() {}
}

Forgetting override

Kotlin requires the override keyword when implementing or replacing an inherited member.

interface Printable {
    fun print()
}

class Report : Printable {
    // Incorrect: missing override
    fun print() {}
}

Correct:

class Report : Printable {
    override fun print() {}
}

Trying to extend two classes

Kotlin allows only one superclass.

open class First
open class Second

// Incorrect
// class Child : First(), Second()

Extract shared behavior into an interface, use composition, or choose one base class.

Ignoring conflicting interface default implementations

If two interfaces provide the same method implementation, the class must resolve the conflict explicitly.

interface A {
    fun greet() = println("A")
}

interface B {
    fun greet() = println("B")
}

class C : A, B {
    override fun greet() {
        super<A>.greet()
    }
}

Comparisons

FeatureJavaKotlin
Extend a classextends Parent: Parent()
Implement an interfaceimplements Contract: Contract
Extend a class and implement interfacesextends Parent implements A, B: Parent(), A, B
Number of direct superclassesOneOne
Number of implemented interfacesManyMany
Override keywordOptional annotation, depending on compiler checksRequired: override

A superclass and an interface serve different purposes:

ChooseWhen you need
Abstract classShared state, constructor parameters, or partial implementation
InterfaceA contract that many unrelated classes can implement
CompositionReusable behavior without making one type inherit from another
Take Quiz

Cheat Sheet

// One superclass and one interface
class Child : Parent(), Contract {
    override fun requiredFunction() {}
}

// One superclass and multiple interfaces
class Child : Parent(), ContractA, ContractB

// Parent constructor with arguments
class Child(id: Int) : Parent(id), Contract

// Interface only
class Child : ContractA, ContractB

Rules:

  • A class may extend one direct class.
  • A class may implement many interfaces.
  • Put the superclass first in the colon list.
  • Use () for a superclass constructor call.
  • Do not use () after a normal interface name.
  • Implement inherited abstract members with override.
  • Resolve duplicate interface default methods with super<InterfaceName>.method().

FAQ

Can a Kotlin class extend a class and implement an interface?

Yes. Use one colon and list the superclass followed by interfaces:

class MyClass : SuperClass(), MyInterface

Why does the superclass have parentheses in Kotlin?

The parentheses call the superclass constructor. Even a no-argument constructor is written as SuperClass().

Does an interface need parentheses in Kotlin inheritance syntax?

No. Use MyInterface, not MyInterface().

Can an abstract Kotlin class implement an interface?

Yes. It can implement all members, some members, or leave abstract members for subclasses to implement.

Can Kotlin extend multiple classes?

No. Kotlin supports one direct superclass, but a class can implement multiple interfaces.

Must I implement every interface function immediately?

A non-abstract class must implement all required abstract members. An abstract class may leave some members unimplemented.

What happens if the superclass and an interface define the same function?

A superclass implementation normally takes precedence. If multiple interfaces provide conflicting default implementations, override the function and choose which implementation to call with super<InterfaceName>.function().

Take Quiz

Mini Project

Description

Build a small notification system. A base class supplies shared recipient information, while an interface defines the contract for sending a notification. This reflects a common design in applications that support email, SMS, push, or other delivery channels.

Goal

Create an email notification class that extends an abstract base class and implements a notification interface.

Requirements

  1. Create an abstract BaseNotification class that stores a recipient.
  2. Create a Sendable interface with a send function.
  3. Create an EmailNotification class that extends the base class and implements the interface.
  4. Validate that the message is not blank before sending it.
  5. Create and send one email notification from main.
Take Quiz

Keep learning

Related questions

View more