# Interfaces
An interface describes the concept of a type. It is a prototype for all classes that implement the interface.
It describes what a class should do, but not how it should do it. An interface provides a form, but generally no implementation. It specifies an object’s actions without detailing how those actions are performed. The interface describes the mission or goal of an entity, versus a class that contains implementation details.
One dictionary definition says that an interface is “The place at which independent and often unrelated systems meet and act on or communicate with each other.” Thus, an interface is a means of communication between different parts of a system.
An Application Programming Interface (API) is a set of clearly defined communication paths between various software components. In object-oriented programming, the API of an object is the set of public members it uses to interact with other objects.
Code using a particular interface only knows what functions can be called for that interface. The interface establishes a “protocol” between classes. (Some object-oriented languages have a keyword called protocol to do the same thing.)
To create an interface, use the interface
keyword instead of the class
keyword. When defining a class that implements an interface, follow the class name with a :
(colon) and the name of the interface:
// Interfaces/Computer.kt
package interfaces
import atomictest.*
interface Computer {
fun prompt(): String
fun calculateAnswer(): Int
}
class Desktop : Computer {
override fun prompt() = "Hello!"
override fun calculateAnswer() = 11
}
class DeepThought : Computer {
override fun prompt() = "Thinking..."
override fun calculateAnswer() = 42
}
class Quantum : Computer {
override fun prompt() = "Probably..."
override fun calculateAnswer() = -1
}
fun main() {
val computers = listOf(
Desktop(), DeepThought(), Quantum()
)
computers.map { it.calculateAnswer() } eq
"[11, 42, -1]"
computers.map { it.prompt() } eq
"[Hello!, Thinking..., Probably...]"
}
Computer
declares prompt()
and calculateAnswer()
but provides no implementations. A class that implements the interface must provide bodies for all the declared functions, making those functions concrete. In main()
you see that different implementations of an interface express different behaviors via their function definitions.
When implementing a member of an interface, you must use the override
modifier. override
tells Kotlin you are intentionally using the same name that appears in the interface (or base class)—that is, you aren’t accidentally overriding.
An interface can declare properties. These must be overridden in all classes implementing that interface:
// Interfaces/PlayerInterface.kt
package interfaces
import atomictest.eq
interface Player {
val symbol: Char
}
class Food : Player {
override val symbol = '.'
}
class Robot : Player {
override val symbol get() = 'R'
}
class Wall(override val symbol: Char) : Player
fun main() {
listOf(Food(), Robot(), Wall('|')).map {
it.symbol
} eq "[., R, |]"
}
Each subclass overrides the symbol
property in a different way:
Food
directly replaces thesymbol
value.Robot
has a custom getter that returns the value (see [Property Accessors](javascript:void(0))).Wall
overridessymbol
inside the constructor argument list (see [Constructors](javascript:void(0)))
An enumeration can implement an interface
:
// Interfaces/Hotness.kt
package interfaces
import atomictest.*
interface Hotness {
fun feedback(): String
}
enum class SpiceLevel : Hotness {
Mild {
override fun feedback() =
"It adds flavor!"
},
Medium {
override fun feedback() =
"Is it warm in here?"
},
Hot {
override fun feedback() =
"I'm suddenly sweating a lot."
},
Flaming {
override fun feedback() =
"I'm in pain. I am suffering."
}
}
fun main() {
SpiceLevel.values().map { it.feedback() } eq
"[It adds flavor!, " +
"Is it warm in here?, " +
"I'm suddenly sweating a lot., " +
"I'm in pain. I am suffering.]"
}
The compiler ensures that each enum
element provides a definition for feedback()
.
# SAM Conversions
The Single Abstract Method (SAM) interface comes from Java, where they call member functions “methods.” Kotlin has a special syntax for defining SAM interfaces: fun interface
. Here we show SAM interfaces with different parameter lists:
// Interfaces/SAM.kt
package interfaces
fun interface ZeroArg {
fun f(): Int
}
fun interface OneArg {
fun g(n: Int): Int
}
fun interface TwoArg {
fun h(i: Int, j: Int): Int
}
When you say fun interface
, the compiler ensures there is only a single member function.
You can implement a SAM interface in the ordinary verbose way, or by passing it a lambda; the latter is called a SAM conversion. In a SAM conversion, the lambda becomes the implementation for the single method in the interface. Here we show both ways to implement the three interfaces:
// Interfaces/SAMImplementation.kt
package interfaces
import atomictest.eq
class VerboseZero : ZeroArg {
override fun f() = 11
}
val verboseZero = VerboseZero()
val samZero = ZeroArg { 11 }
class VerboseOne : OneArg {
override fun g(n: Int) = n + 47
}
val verboseOne = VerboseOne()
val samOne = OneArg { it + 47 }
class VerboseTwo : TwoArg {
override fun h(i: Int, j: Int) = i + j
}
val verboseTwo = VerboseTwo()
val samTwo = TwoArg { i, j -> i + j }
fun main() {
verboseZero.f() eq 11
samZero.f() eq 11
verboseOne.g(92) eq 139
samOne.g(92) eq 139
verboseTwo.h(11, 47) eq 58
samTwo.h(11, 47) eq 58
}
Comparing the “verbose” implementations to the “sam” implementations you can see that SAM conversions produce much more succinct syntax for a commonly-used idiom, and you aren’t forced to define a class to create a single object.
You can pass a lambda where a SAM interface is expected, without first wrapping it into an object:
// Interfaces/SAMConversion.kt
package interfaces
import atomictest.trace
fun interface Action {
fun act()
}
fun delayAction(action: Action) {
trace("Delaying...")
action.act()
}
fun main() {
delayAction { trace("Hey!") }
trace eq "Delaying... Hey!"
}
In main()
we pass a lambda instead of an object that implements the Action
interface
. Kotlin automatically creates an Action
object from this lambda.
Exercises and solutions can be found at www.AtomicKotlin.com.