Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Properties in Interfaces

We noted previously that interfaces cannot contain members whose purpose is to supply objects with some of their state. An interface can still specify properties, but those properties are implicitly abstract.

Just like the abstract methods of an interface, those abstract properties will need to be implemented in any class that implements the interface.

Here’s a small example of an interface that represents users of a computer system:

interface User {
    val username: String
}

The interface User does not actually provide a username property that other classes can use. Instead, User imposes a contractual obligation on any classes that implement it, requiring them to provide a property with this name, of type String. That property could have backing storage, or it could be a computed property, but it has to exist in order for a class that implements User to compile successfully.

For example, we could have a class LocalUser, representing local users of the system. In this case, username is overridden with a regular String property that has backing storage:

class LocalUser(override val username: String) : User

This implementation follows the compact class definition syntax that you have seen earlier, but note the use of the override keyword. This is required by the compiler.

In addition to LocalUser, we could have another class SubscribingUser, representing external users who have registered with the system using their email address:

class SubscribingUser(val email: String) : User {
    override val username: String
        get() = email.substringBefore('@')
}

A SubscribingUser has an email property with backing storage, representing the email address with which a user subscribed. It also has a username property, as required by the User interface, but in this case that property has no backing storage; instead, it is computed on demand from the email address.

With the interface and classes shown above, tasks such as displaying the usernames of all system users become very straightforward:

val users = mutableListOf<User>()
...
users.forEach {
    println(it.username)
}

This is polymorphic code, much like that in the graphics application discussed earlier.