Interfaces
Prerequisites: Inheritance
An interface describes what a class can do — a set of methods and properties — without saying how. Any class can implement as many interfaces as it likes.
Real-world analogy: an interface is a job description ("must be able to drive, must have a license"). Anyone who meets it can do the job, whatever their background.
Example
interface Notifier {
val channel: String
fun send(message: String): String
fun sendAll(messages: List<String>) = messages.map { send(it) }
}
class EmailNotifier(val address: String) : Notifier {
override val channel = "email"
override fun send(message: String) = "Email to $address: $message"
}
class SmsNotifier : Notifier {
override val channel = "sms"
override fun send(message: String) = "SMS: ${message.take(20)}"
}
fun main() {
val notifiers: List<Notifier> = listOf(EmailNotifier("ada@example.com"), SmsNotifier())
for (n in notifiers) {
println("[${n.channel}] ${n.send("Your order has shipped today")}")
}
println(SmsNotifier().sendAll(listOf("Hi", "Bye")))
}
Expected output
[email] Email to ada@example.com: Your order has shipped today
[sms] SMS: Your order has shipp
[SMS: Hi, SMS: Bye]
- An interface can declare abstract properties (
channel) and methods (send) that implementing classes must provide. - It can also provide default implementations —
sendAllworks for every notifier without being written again. - Code that uses a
Notifierdoesn't care which kind it has. Adding aSlackNotifierlater needs no change to that code.
Interfaces make code testable
Depending on an interface instead of a concrete class lets tests pass a
fake implementation — for example, a FakeNotifier that records
messages instead of sending them. You'll use this in the Testing lesson.
Functional interfaces
An interface with a single abstract method, marked fun interface, can
be created from a lambda:
fun interface Validator {
fun isValid(input: String): Boolean
}
fun main() {
val notBlank = Validator { it.isNotBlank() }
println(notBlank.isValid(" "))
}
false
Common mistakes
- Forgetting
overrideon implemented members. - Designing one huge interface. Small, focused interfaces are easier to implement and combine.