Author profile picture

Examples of Classes in Kotlin

One core concept in Kotlin is a class, which allows you to combine variables and functions into a single unit. This article is a quick reference that shows how to create classes in Kotlin. There’s also a full article called Introduction to Classes and Objects, which is a gentle introduction to these concepts, so if you’re new to the language, you might want to start there!

Simple class in UML

A Simple Class

For a class that has no properties or functions, you can create a class in Kotlin with very little code:

class Button
Properties

Adding Properties to a Class

You can add public properties to a class by putting them inside parentheses after the name.

class Button(val label: String)

Just like with variables , val will make the property read-only, and var will make the property read-write.

class Button(var label: String)

You can put visibility modifiers on the properties if, for example, you want the property to have private access:

class Button(private var label: String)

Properties declared inside the parentheses like require you to provide values for them when you instantiate the class. But, you can provide a default argument value, like this:

class Button(val label: String = "Click me")

You don’t have to put the properties on a single line with the class. Kotlin developers often put them on separate lines.

class Button(
    val label: String = "Click me"
)

This can be easier to read, especially when you’ve got lots of properties.

class Button(
    val width: Int,
    val height: Int,
    val label: String = "Click me"
)

If you want to never provide a value for a property during construction, you can put the property inside a class body instead.

class Button(val label: String) {
    var width: Int = 350
    var height: Int = 75
}

You can add a constructor parameter without making it a property. Just omit the val or var, as done with width and height here:

class Button(val label: String, width: Int, height: Int) {
    val area = width * height
}

Properties defined inside a class body can also have access modifiers.

class Button(val label: String, width: Int, height: Int) {
    private val area = width * height
}

If you don’t need to define any properties in the constructor, you can omit the parentheses and everything in between.

class Button {
    var width: Int = 350
    var height: Int = 75
}

Properties defined in the class body can have public getters along with private setters. You can think of this as if we’re making a property val outside of the class and var inside the class.

To do this, just add private set on the line after the property.

class Button {
    var width: Int = 350
        private set
    var height: Int = 75
        private set
}

Alternatively, you can put them on the same line if you use a semicolon.

class Button {
    var width: Int = 350; private set
    var height: Int = 75; private set
}
Functions

Adding Functions to a Class

Add a function to a class by inserting it into the class body.

class Button(private val label: String) {
    fun printDescription() = println(label)
}

Functions in classes can also have access modifiers on them.

class Button(private val label: String) {
    internal fun printDescription() {
        println(label)
    }
}
Extending classes

Extending Classes

One class can extend another class, to add more properties and functions, or to specialize its behavior. Sometimes we call this “subclassing”.

In Kotlin, classes are final by default, which means that you cannot extend a class unless you explicitly indicate that it is open to extension, by adding the open modifier:

open class Button(val label: String)

Extend an open class by invoking its constructor when you create the subclass. You’ll need to provide any arguments that are required by the constructor of the superclass (that is, the class that you’re extending), like this:

open class Button(val label: String)

// Passes the `label` argument through to the Button constructor
class ImageButton(label: String, val image: Image): Button(label)

// Provides a default value for the `label`.
class DefaultButton : Button("Click me")

Just as classes are final by default, so are functions and properties in a class.

If you want a subclass to do something different from the superclass:

  1. The superclass must mark the function or property as open, and…
  2. The subclass must mark the function or property as override.
open class Button(val label: String) {
    open fun printDescription() {
        println(label)
    }
}

class ImageButton(label: String, val image: Image): Button(label) {
    override fun printDescription() {
        println("$label with image ${image.description}")
    }
}

Abstract Classes

A class can be marked abstract. Classes that are abstract do not allow you to instantiate them directly; you can only instantiate one of their subclasses.

abstract class AbstractButton(val label: String)

Abstract classes allow you to declare a function or property, but punt its definition to the subclass. Subclasses must still use the override modifier.

abstract class AbstractButton(val label: String) {
    abstract fun printDescription()
}

class Button(label: String) : AbstractButton(label) {
    override fun printDescription() {
        println(label)
    }
}
Implementing interfaces

Implementing Interfaces

A class can implement an interface. This looks almost identical to extending a class, except that interfaces have no constructors to invoke.

interface Button {
    val label: String
}

class SimpleButton(override val label: String): Button

Classes can implement multiple interfaces.

interface View {
    val width: Int
    val height: Int
}

interface Button {
    val label: String
}

class SimpleButton(
    override val width: Int,
    override val height: Int,
    override val label: String
): View, Button

A class can both implement interfaces and extend a single class.

interface View {
    val width: Int
    val height: Int
}

abstract class Button(val label: String) {
    abstract fun printDescription()
}

class SimpleButton(label: String): View, Button(label) {
    override val width: Int = 350
    override val height: Int = 75

    override fun printDescription() {
        println(label)
    }
}
Generic classes

Generic Classes

Add a type parameter to make a class generic.

class Box<T>(val item: T)

Multiple type parameters can be separated with a comma.

abstract class Converter<T, R> {
    abstract fun convert(input: T): R
}

You can add an upper bound constraint to a type parameter with a concise syntax…

class PrintJob<T : Printable>(val item: T)

… or with a more verbose syntax…

class PrintJob<T>(val item: T) where T : Printable

You can use the more verbose syntax to add multiple constraints to the same type parameter.

class Document<T>(val item: T) where T : Printable, T: Serializable

Modify a type parameter with the out keyword to establish covariance

class Box<out T>(val item: T)

… or with the in keyword to establish contravariance

class Box<in T>(private var item: T) {
    fun changeItem(newItem: T) {
        item = newItem
    }
}

Share this article:

  • Share on Twitter
  • Share on Facebook
  • Share on Reddit