Interfaces
In the first decade or so of Java, there was a clear distinction between abstract classes and interfaces:
- Abstract classes could have concrete methods and fields, along with abstract methods delegated to subclasses to implement
- Interfaces were purely a set of abstract methods, used for defining a contract
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:
- the
Aquatic
interface, to be used for animals that live primarily in the water - a base
Animal
class that isopen
for subclasses - an
AquaticAnimal
that extendsAnimal
and also implements theAquatic
interface -
Frog
andAxolotl
extendingAquaticAnimal
and implementing the abstracthasGills()
function
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.