Kotlin Interfaces

Interfaces

Like abstract classes, interfaces cannot be instantiated directly. They define properties and methods that must be implemented. If an abstract class is mainly for “inheritance”, an interface defines the “capabilities” of a subclass, and a single subclass can implement multiple interfaces.

Kotlin interfaces can contain abstract methods and method implementations. The difference from abstract classes is that interfaces cannot store property state.

In other words, properties in an interface cannot have initializers.

interface User {
    val name = "devkuma" // Error: property initializers are not allowed in interfaces.
}

Interfaces are defined with the interface keyword.

interface MyInterface {
    fun bar()
    fun foo() {
      // optional body
    }
}

Implementing Interfaces

A class or object can implement one or more interfaces. When it inherits an interface, it must implement the interface members.

class Child : MyInterface {
    override fun bar() {
        // body
    }
}

Properties in Interfaces

Properties can be declared in interfaces. A property declared in an interface must either be abstract or provide an accessor implementation. Because properties cannot be initialized in interfaces, accessors declared in interfaces cannot refer to backing state.

interface MyInterface {
    val prop: Int // abstract

    val propertyWithImplementation: String
        get() = "foo"

    fun foo() {
        print(prop)
    }
}

class Child : MyInterface {
    override val prop: Int = 29
}

Interfaces Inheritance

Interfaces can inherit from other interfaces. Therefore, an inheriting interface can implement members from the parent interface or declare new methods and properties.

A class that implements such an interface only needs to implement the members that remain unimplemented.

interface Named {
    val name: String
}

interface Person : Named {
    val firstName: String
    val lastName: String

    override val name: String get() = "$firstName $lastName"
}

data class Employee(
    // implementing 'name' is not required
    override val firstName: String,
    override val lastName: String,
    val position: Position
) : Person

If the name: String property from the Named interface had not been implemented in Person, it would need to be implemented in Employee.

Resolving overriding conflicts

When implementing multiple interfaces, the same methods may be inherited from more than one interface.

interface A {
    fun foo() { println("A") }
    fun bar() // abstract
}

interface B {
    fun foo() { println("B") }
    fun bar() { println("bar") }
}

class C : A {
    override fun bar() { println("bar") }
}

class D : A, B {
    override fun foo() {
        super<A>.foo()
        super<B>.foo()
    }

    override fun bar() {
        super<B>.bar()
    }
}

fun main() {
    val c = C()
    c.foo()
    c.bar()

    println()

    val d = D()
    d.foo()
    d.bar()
}

Interfaces A and B both declare the foo() and bar() functions. Both A and B implement foo(). A does not implement bar(), but B does.

Class C inherits only from interface A, so it only needs to implement bar().

Class D inherits from interfaces A and B, so it needs to explicitly handle the duplicated methods. This applies both to the bar() function, which has only one implementation, and the foo() function, which has multiple implementations.

References