Kotlin – 16 – Interfaces in Kotlin

In Kotlin, interfaces are a fundamental part of object-oriented programming that allow you to define a contract or a set of rules that classes must adhere to. Interfaces provide a way to achieve abstraction and ensure that classes implement certain methods. In this guide, we’ll explore interfaces in Kotlin, how to define them, how they differ from abstract classes, and their practical applications.

Defining an Interface

In Kotlin, an interface is defined using the interface keyword, followed by the name of the interface. Interfaces can contain method declarations, property declarations, or abstract properties (properties without an initial value). Here’s the basic syntax of an interface:

interface MyInterface {
    fun myMethod()
    val myProperty: Int
}

In this example, we’ve defined an interface named MyInterface with a method myMethod() and an abstract property myProperty. Any class that implements this interface must provide implementations for these members.

Implementing an Interface

To implement an interface in Kotlin, a class uses the : symbol followed by the name of the interface in the class header. The class must provide concrete implementations of all the methods and properties declared in the interface. Here’s an example:

interface MyInterface {
    fun myMethod()
}

class MyClass : MyInterface {
    override fun myMethod() {
        println("Method implementation in MyClass")
    }
}

In this example, MyClass implements MyInterface and provides an implementation for the myMethod() declared in the interface.

Multiple Interfaces

Kotlin allows a class to implement multiple interfaces, separated by commas. This feature is useful when you want to enforce multiple contracts or take advantage of different sets of functionality. Here’s an example:

interface Drawable {
    fun draw()
}

interface Clickable {
    fun onClick()
}

class Button : Drawable, Clickable {
    override fun draw() {
        println("Drawing button")
    }

    override fun onClick() {
        println("Button clicked")
    }
}

In this example, the Button class implements both the Drawable and Clickable interfaces, providing implementations for their respective methods.

Properties in Interfaces

Interfaces can also declare properties, including abstract properties that require implementing classes to provide getter or setter implementations. Here’s an example with a property in an interface:

interface Printable {
    val message: String
    fun printMessage()
}

class MyPrinter : Printable {
    override val message: String = "Hello, Kotlin"

    override fun printMessage() {
        println(message)
    }
}

In this example, the Printable interface declares an abstract property message, and the MyPrinter class implements this property with a concrete value.

Differences from Abstract Classes

While interfaces in Kotlin are similar to abstract classes, there are some key differences:

  1. Multiple Inheritance: Unlike abstract classes, which allow a class to inherit from only one superclass, a class can implement multiple interfaces. This provides more flexibility when designing class hierarchies.
  2. No State: Interfaces cannot contain state or fields (properties with backing fields) like abstract classes can. They can only declare properties without initial values and methods.
  3. No Constructors: Interfaces cannot have constructors, while abstract classes can define constructors.
Practical Use Cases

Interfaces are commonly used in Kotlin and other object-oriented languages to achieve abstraction, enforce contracts, and enable code reuse. Here are some practical use cases for interfaces:

  • API Design: When designing libraries or frameworks, interfaces define the contracts that users of the library must adhere to. This promotes consistency and makes it clear how to interact with the library’s components.
  • Dependency Injection: In the context of dependency injection, interfaces are used to define abstractions for services or dependencies. This allows you to switch implementations easily, making your code more modular and testable.
  • Polymorphism: Interfaces enable polymorphism, allowing different classes to be treated as instances of a common interface. This is useful for writing generic code that works with various objects.
  • Plugin Systems: In plugin-based architectures, interfaces are often used to define the expected behavior of plugins. Each plugin must implement a specific interface, ensuring that it integrates seamlessly with the host application.
Command and Example

Here’s a complete example demonstrating the use of interfaces in Kotlin:

interface Shape {
    fun area(): Double
}

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

class Rectangle(val width: Double, val height: Double) : Shape {
    override fun area(): Double {
        return width * height
    }
}

fun main() {
    val circle = Circle(5.0)
    val rectangle = Rectangle(4.0, 6.0)

    val shapes: List<Shape> = listOf(circle, rectangle)
    for (shape in shapes) {
        println("Area: ${shape.area()}")
    }
}

In this example, we have an Shape interface with a single method area(). Two classes, Circle and Rectangle, implement this interface and provide their own implementations of the area() method. The main() function demonstrates polymorphism by creating a list of Shape objects and calculating and printing their respective areas, showcasing how interfaces allow objects of different classes to be treated uniformly.