Kotlin Interfaces

If you’re familiar with Java, Kotlin interfaces are similar to Java 8 interfaces. If you know Scala, they also have similarities to Scala traits.

Key points

What you’ll see in this lesson:

  • Interfaces are defined using the interface keyword
  • Interfaces can declare functions, which can be abstract or concrete
  • Interfaces can declare fields (properties):
    • They can be abstract
    • They can provide implementations for accessors
  • Interfaces can inherit/derive from other interfaces
  • A class or object can implement one or more interfaces
  • When you inherit from multiple interfaces that have methods with the same names and signatures, you must manually resolve the conflict

As shown previously, Kotlin also has a concept of abstract classes. Use abstract classes instead of interfaces when:

  • You need constructor parameters (interfaces can’t have them)
  • You need to define concrete read/write fields
  • You need private members

Interfaces with abstract and concrete functions

This example shows common uses of interfaces, with both abstract and concrete functions in the interfaces, along with overridden functions in the concrete Dog class:

package interfaces

interface Speaker {
    //abstract function
    fun speak(): String
}

interface TailWagger {
    // concrete implementations
    fun startTail() { println("tail is wagging") }
    fun stopTail() { println("tail is stopped") }
}

class Dog : Speaker, TailWagger {

    // override an abstract function
    override fun speak(): String = "Woof!"

    // override a concrete function
    override fun stopTail() { println("can’t stop the tail!") }

}

fun main(args: Array<String>) {
    val d = Dog()
    println(d.speak())  //"Woof!"
    d.startTail()       //"tail is wagging"
    d.stopTail()        //"can’t stop the tail!"
}

Properties

Key points:

  • Interfaces can define properties (fields)
  • They can be abstract
  • You can define an accessor (getter) for a property

Examples:

interface PizzaInterface {
    var numToppings: Int      //abstract
    var size: Int             //abstract
    val maxNumToppings: Int   //concrete
        get() = 10
}

class Pizza : PizzaInterface {

    // simple override
    override var numToppings = 0

    // override with get/set
    override var size: Int = 14  //14"
        get() = field
        set(value) { field = value}

    // override on a `val`
    override val maxNumToppings: Int
        //get() = super.maxNumToppings
        get() = 20
}


fun main(args: Array<String>) {
    val p = Pizza()
    println(p.numToppings)     //0
    println(p.size)            //14
    println(p.maxNumToppings)  //20

    p.numToppings = 5
    p.size = 16
}

A note from the official Kotlin docs: “A property declared in an interface can either be abstract, or it can provide implementations for accessors. Properties declared in interfaces can’t have backing fields, and therefore accessors declared in interfaces can’t reference them.”

Inheritance

Interfaces can extend other interfaces, override their properties and functions, and declare new properties and functions:

interface StarTrekCrewMember {
    fun uniformColor(): String
}

interface Officer : StarTrekCrewMember {
    override fun uniformColor(): String = "not red"
}

interface RedShirt : StarTrekCrewMember {
    override fun uniformColor(): String = "red (sorry about your luck)"
    fun diePainfulDeath(): String = "i’m dead"
}

// more code ...

Here’s a class that implements three interfaces:

interface Starship
interface WarpCore
interface WarpCoreEjector
class Enterprise : Starship, WarpCore, WarpCoreEjector

Resolving inheritance conflicts

When a class extends multiple interfaces and those interfaces have a common method, you must manually handle the situation in the function in your class. This example shows how to handle the functions foo and bar in the class C, where C extends both interfaces A and B:

interface A {
    fun foo() { println("foo: A") }
    fun bar(): Unit
}

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

class C : A, B {
    override fun foo() {
        super<A>.foo()
        super<B>.foo()
        println("foo: C")
    }
    override fun bar() {
        super<B>.bar()
        println("bar: C")
    }
}

fun main(args: Array<String>) {
    val c = C()
    c.foo()
    println()
    c.bar()
}

Here’s the output from main:

foo: A
foo: B
foo: C

bar: B
bar: C

This example is a simplified version of this kotlinlang.org example.

results matching ""

    No results matching ""