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!
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
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, andvar
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
orvar
, as done withwidth
andheight
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 andvar
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 }
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
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:
- The superclass must mark the function or property as
open
, and…- 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
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
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 } }
Related Articles