Interfaces

In the first decade or so of Java, there was a clear distinction between abstract classes and interfaces:

Java 8 started to blur the lines, allowing interfaces to offer “default” methods (concrete methods designed to be overridden as needed).

Kotlin offers its own take on interfaces, which largely mirrors Java 8’s approach.

Basic Definition

Declaring an interface in Kotlin is similar to how you declare them in Java: replace class with interface and (usually) have 1+ members (e.g., functions). You do not need the abstract keyword for functions without bodies, as it is assumed that those are abstract by default:

interface Aquatic {
  fun hasGills(): Boolean
}

However, there is no requirement for an interface to have any abstract members, though, so this is legal:

interface Aquatic {
  fun hasGills() = false
}

Basic Usage

In Java, having a class implement an interface starts with the implements keyword:

class Animal implements Aquatic {
  // TODO stuff goes here
}

In Kotlin, interfaces are just listed alongside any superclasses in a comma-delimited list after the : in the class declaration:

interface Aquatic {
  fun hasGills(): Boolean
}

open class Animal

abstract class AquaticAnimal : Animal(), Aquatic {
  fun description() = "hasGills = ${hasGills()}"
}

class Frog(val hasGillsRightNow: Boolean) : AquaticAnimal() {
  override fun hasGills() = hasGillsRightNow
}

class Axolotl : AquaticAnimal() {
  override fun hasGills() = true
}

fun main() {
  println(Axolotl().description())
  println(Frog(true).description())
}

Here, we have:

Note that AquaticAnimal needs to be declared as abstract, as while it “implements” Aquatic, it does not satisfy the Aquatic contract by implementing hasGills(). Since hasGills() is being deferred to subclasses to implement, we need to make AquaticAnimal be an abstract class. We do not need to repeat the abstract fun hasGills(): Boolean declaration, though — the one that we get from Aquatic is sufficient.

Also note that in the list of classes and interfaces as part of the AquaticAnimal declaration, classes use constructor notation (parentheses and, if applicable, parameters to pass to the superclass’ constructor). Interfaces, though, are just the name of the interface, without parentheses.

Using Multiple Interfaces

Kotlin does not allow a class to extend from multiple superclasses. However, Kotlin does allow a class to implement multiple interfaces, just by adding them to the declaration list:

interface Aquatic {
  fun hasGills(): Boolean
}

interface ReproductionMode {
  fun isOviparous(): Boolean     // i.e., does it lay eggs?
}

open class Animal

abstract class AquaticAnimal : Animal(), Aquatic, ReproductionMode {
  fun description() = "hasGills = ${hasGills()}, isOviparous() = ${isOviparous()}"
}

class Frog(val hasGillsRightNow: Boolean) : AquaticAnimal() {
  override fun hasGills() = hasGillsRightNow
  override fun isOviparous() = true
}

class Axolotl : AquaticAnimal() {
  override fun hasGills() = true
  override fun isOviparous() = true
}

class Orca : AquaticAnimal() {
  override fun hasGills() = false
  override fun isOviparous() = false
}

class KomodoDragon : Animal(), ReproductionMode {
  override fun isOviparous() = true
}

fun main() {
  println(Axolotl().description())
  println(Frog(true).description())
  println(Orca().description())
}

Here, we have AquaticAnimal implement both Aquatic and ReproductionMode.

Of course, since all animals reproduce, we could have Animal implement ReproductionMode and simplify this a bit, though it will mean that by default Animal would need to be abstract:

interface Aquatic {
  fun hasGills(): Boolean
}

interface ReproductionMode {
  fun isOviparous(): Boolean     // i.e., does it lay eggs?
}

abstract class Animal : ReproductionMode

abstract class AquaticAnimal : Animal(), Aquatic

class Frog(val hasGillsRightNow: Boolean) : AquaticAnimal() {
  override fun hasGills() = hasGillsRightNow
  override fun isOviparous() = true
}

class Axolotl : AquaticAnimal() {
  override fun hasGills() = true
  override fun isOviparous() = true
}

class Orca : AquaticAnimal() {
  override fun hasGills() = false
  override fun isOviparous() = false
}

class KomodoDragon : Animal() {
  override fun isOviparous() = true
}

Bear in mind that any abstract class is also open, and so we could default the implementation of interface functions in superclasses, to reduce redundancy a bit:

interface Aquatic {
  fun hasGills(): Boolean
}

interface ReproductionMode {
  fun isOviparous(): Boolean     // i.e., does it lay eggs?
}

abstract class Animal : ReproductionMode {
  override fun isOviparous() = false
}

abstract class AquaticAnimal : Animal(), Aquatic {
  override fun hasGills() = true
}

class Frog(val hasGillsRightNow: Boolean) : AquaticAnimal() {
  override fun hasGills() = hasGillsRightNow
  override fun isOviparous() = true
}

class Axolotl : AquaticAnimal() {
  override fun isOviparous() = true
}

class Orca : AquaticAnimal() {
  override fun hasGills() = false
}

class KomodoDragon : Animal() {
  override fun isOviparous() = true
}

Now Frog, Axolotl, Orca, and KomodoDragon only need to override those functions where their needs differ from the default supplied by Animal or AquaticAnimal.

Concrete Functions

Not only can interfaces have functions marked as abstract, but they can have regular concrete functions as well. As this sample shows, interfaces do not necessarily need any abstract functions at all:

interface Aquatic {
  fun hasGills() = true
}

interface ReproductionMode {
  fun isOviparous() = false     // i.e., does it lay eggs?
}

abstract class Animal : ReproductionMode

abstract class AquaticAnimal : Animal(), Aquatic

class Frog(val hasGillsRightNow: Boolean) : AquaticAnimal() {
  override fun hasGills() = hasGillsRightNow
  override fun isOviparous() = true
}

class Axolotl : AquaticAnimal() {
  override fun isOviparous() = true
}

class Orca : AquaticAnimal() {
  override fun hasGills() = false
}

class KomodoDragon : Animal() {
  override fun isOviparous() = true
}

Here, rather than putting the default implementations of hasGills() and isOviparous() on the Animal and AquaticAnimal classes, we put them directly on the interfaces themselves.


Prev Table of Contents Next

This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.