Kotlin

Kotlin is a modern, statically-typed programming language that runs on the Java Virtual Machine (JVM). It was developed by JetBrains and first released in 2011.

All examples are based on Kotlin 1.8.0.

Resources:

How to start

A simple and fast way to get started with Kotlin is to use an IDE like IntelliJ IDEA.

//main.kt
fun main() {
    println("Hello, world")
}

Topics

Variables

//var: mutable variable
var x = 10

//val: immutable variable
val y = 10

//typed variable
val z: Int = 10

//nullable variable
val a: Int? = null

Primitive Types

val a: String = "Hello"
val b: Int = 10
val c: Double = 3.14
val d: Boolean = true
val f: Byte = 100
val g: Short = 1000
val h: Long = 100000
val i: Float = 10.0F
val j: Char = 'a'
val k: CharSequence = "Hello"

Any type: is a variable of any type

val any: Any = "Hello"
any = 10
any = true

Operators

//Arithmetic Operators
val a = 10 + 20
val b = 10 - 20
val c = 10 * 20
val d = 10 / 20
val e = 10 % 20

//Comparison Operators
val a = 10 == 20
val b = 10 != 20
val c = 10 > 20
val d = 10 < 20
val e = 10 >= 20
val f = 10 <= 20

//Logical Operators
val a = true && false
val b = true || false

//Assignment Operators
var a = 10
a += 20
a -= 20
a *= 20
a /= 20
a %= 20

//Conditional Operators
val a = 10
a ?: 20

//Range Operators
val a = 10..20
for (i in a) { println(i)}

//Type test
if (a is Int) { println("a is Int") }
if (a is String) { println("a is String") }

//Bitwise Operators
val a = 10 and 20
val b = 10 or 20
val c = 10 xor 20
val d = 10 shl 20
val e = 10 shr 20
val f = 10 ushr 20

//Elvis operator
val a = null
var b = a ?: "default"

Strings

//single line string
var a = "Hello world"

//multiline string
var b = """
    Hello
    world
"""

//string interpolation
var c = "Hello $a"

//string template
fun message(): String {  return "Hello World!" }
println("message: ${message()}")

Null Safety

Null safety is a feature in Kotlin that helps prevent null pointer exceptions. It allows you to declare variables as nullable, and then handle the case where the variable is null.

//null-nullable variable
val a: String? = null

//null nullable variable
val b: String = a ?: "default"

//null-aware operator (safe call operator)
val c = a?.length

//null assertion operator
val d = a!!.length

//check for null in conditions
val e = if (a != null) { a.length } else { 0 }

//elvis operator with nullable receiver
val l = b?.length ?: 0

//safe casts
val f: String = a as? Int

Operator Overloading

Operator overloading allows you to define custom behavior for specific operators. In Kotlin, you can define operator overloading for arithmetic operators, comparison operators, and more.

data class Point(val x: Int, val y: Int)

operator fun Point.plus(other: Point): Point {
    return Point(this.x + other.x, this.y + other.y)
}

val point = Point(1, 2)
val other = Point(3, 4)

fun main(args: Array<String>) {
    println(point + other) //Point(x=4, y=6)
}

Collections

List: is a simple collection of objects. There is two types of list: mutable and immutable.

val list = listOf("a", "b", "c")
val typedList: List<Int> = listOf(1, 2, 3)
var mutableList = mutableListOf("a", "b", "c")

//some methods
mutableList.add("d")
mutableList.remove("b")
mutableList.clear()
mutableList.addAll(list)
mutableList.first()
mutableList.last()
mutableList.forEach { println(it) }

Set: is an unordered collection of unique elements. There are two types of sets: mutable and immutable.

var set = setOf(1, 2, 3, 4, 5)
var typedSet: Set<Int> = setOf(1, 2, 3, 4, 5)
var mutableSet: MutableSet<Int> = mutableSetOf(1, 2, 3, 4, 5)

//some methods
mutableSet.add(6)
mutableSet.remove(3)
mutableSet.clear()
mutableSet.contains(3)
mutableSet.size
mutableSet.isEmpty()
mutableSet.forEach { println(it) }

Map: is a collection of key-value pairs. There are two types of map: immutable and mutable

val map = mapOf<String, String>()
val typedMap: Map<String, String> = mapOf(
    "key1" to "value1",
    "key2" to "value2"
)
var mutableMap = mutableMapOf<String, String>(
    "key1" to "value1",
    "key2" to "value2"
)

//some methods
mutableMap.put("key3", "value3")
mutableMap.putAll(typedMap)
mutableMap.remove("key1")
mutableMap.clear()
mutableMap.isEmpty()
mutableMap.containsKey("key1")
mutableMap.containsValue("value1")
mutableMap.forEach { println("key: $it.key, value: $it.value") }

Array: Arrays are collections of fixed size that contain elements of the same type. Kotlin supports both mutable and immutable arrays.

val array = arrayOf("a", "b", "c")
println(array)

//some methods
array.size
array.isEmpty()
array.contains("a")
array.contains("d")
array.indexOf("a")
array.lastIndexOf("a")

Sequence: Sequences are special types of collections that lazily compute their elements. They are useful for performing operations on large or infinite collections without eagerly computing all elements at once.

var sequence = sequenceOf(1, 2, 3, 5)

//some methods
sequence.count()
sequence.sum()
sequence.max()
sequence.min()
sequence.average()
sequence.toList()

Range: Ranges are not collections in the traditional sense, but they represent a sequence of values between a start and an end, inclusive or exclusive.

val range = 1..5
println(range)

//some methods
range.count()
range.sum()
range.max()
range.min()
range.average()
range.toList()
Collection TypeMutable ImplementationImmutable Implementation
ListMutableListList
SetMutableSetSet
MapMutableMapMap
ArrayArrayN/A
SequenceN/AN/A
RangeN/AN/A

Conditional Statements

val x = 10

// if statement
if (x > 5) {  println("x is greater than 5") }
else{  println("x is less than or equal to 5") }

//inline if
println(if (x > 5) "x is greater than 5" else "x is less than or equal to 5")

//when expression
when (x) {
    0 -> println("x is 0")
    1 -> println("x is 1")
    2 -> println("x is 2")
    3 -> println("x is 3")
    4 -> println("x is 4")
    5 -> println("x is 5")
    else -> println("x is greater than 5")
}

//when expression with return
val y = when (x) {
    0 -> "x is 0"
    1 -> "x is 1"
    2 -> "x is 2"
    3 -> "x is 3"
    4 -> "x is 4"
    5 -> "x is 5"
    else -> "x is greater than 5"
}
println(y)

Loops

//for loop
for (i in 1..5) {
    println(i)
}

//while loop
var i = 1
while (i <= 5) {
    println(i)
    i++
}

//do-while loop
var i = 1
do {
    println(i)
    i++
} while (i <= 5)

//range loop
for (i in 1..5) {
    println(i)
}

//range loop with step
for (i in 1..5 step 2) {
    println(i)
}

//rage loop with step and reverse
for (i in 6 downTo 0 step 2) {
    println(i)
}

Functions

//function with no parameters
fun sayHello() {
    println("Hello")
}

//function with parameters
fun sayHello(name: String) {
    println("Hello, $name")
}

//function with multiple parameters
fun sayHello(name: String, age: Int) {
    println("Hello, $name, you are $age years old")
}

//function with return type
fun add(a: Int, b: Int): Int {
    return a + b
}

//function with default parameter
fun sayHello(name: String = "World") {
    println("Hello, $name")
}

//infix function:
//is a simple way to extends a class with a new method
infix fun Int.add(i: Int) = this + i
var a: Int = 10
println(a.add(5))

//lambda function:
//is a function that can be passed as an argument to another function
val add = fun(a: Int, b: Int): Int { return a + b }
println(add(5, 10))

//function overloading
fun add(a: Int, b: Int): Int  { return a + b }
fun add(a: String, b: String): Int { return a.toInt() + b.toInt() }

Calling functions with multiple parameters

When a function have multiple parameters, the last parameter can be called with {}.

fun calc(x: Int, y: Int, action: (Int, Int) -> Int): Int
{
    return action(x, y)
}

//calling
var a = calc(5, 10) { a, b -> a + b }
var b = calc(5, 10) { a, b -> a * b }
var c = calc(5, 10) { a, b -> a - b }
var d = calc(5, 10) { a, b -> a / b }

println("a = $a")
println("b = $b")
println("c = $c")
println("d = $d")

Classes

Classes can contain:

  • Constructors and initializer blocks
  • Functions
  • Properties
  • Nested and inner classes
  • Object declarations

Constructors:

//primary constructor
class Person(val name: String, val age: Int)

class Person(name: String, age: Int) {
    var name: String = name
    var age: Int = age
}

//secondary constructor
class Person {
    var name: String
    var age: Int

    constructor(name: String, age: Int) {
        this.name = name
        this.age = age
    }
}

//initializer block
class Person {
    var name: String = "John"
    var age: Int = 25

    init {
        println("Person name: $name")
        println("Person age: $age")
    }
}

//initializer block
class Person(private val name: String, private var age: Int) {
    init {
        println("Person initialized with name: $name and age: $age")
    }
}

Properties, Fields, Getters and Setters:

// default getter and setter are generated
class Person {
    var name: String  = ""
}

//custom getter and setter
class Person {
    var age: Int = 0
        get() = field
        set(value) {
            if (value < 0)
                throw IllegalArgumentException("Age cannot be negative")
        }
}

//backing property
class User {
    private var _email: String = ""

    var email: String
        get() = _email
        set(value) {
            _email = value
            println("Email changed to $value")
        }
}
val user = User()
user.email = "email@gmail.com"

Nested and inner classes

In Kotlin, an inner class is a class declared within another class. Inner classes have a special relationship with the outer class, allowing them to access the members of the outer class, including private members. Here’s an explanation of inner classes in Kotlin:

//outer class members
class Outer {
    // Inner class members
    inner class Inner {
    }
}

//access to outer class members
class Store(private var name: String) {
    inner class Car(private var color: String) {
        fun info(){
            println("Car color $color from store $name")
        }
    }
}
val store = Store("My Store")
val car = store.Car("Red")

car.info()

//qualifier access
//this@Outer: refers to the outer class
class Outer {
    private val outerProperty: Int = 10

    inner class Inner {
        fun accessOuterProperty() {
            println("Outer property value: ${this@Outer.outerProperty}")
        }
    }
}

Object declarations

In Kotlin, object declarations provide a convenient way to define singleton objects, which are objects that have only one instance throughout the entire application.

object Car {
    private var make = "ford"
    private var model = "mustang"
    private var color = "red"
    private var year = 2020

    fun printInfo() {
        println("make: $make, model: $model, color: $color, year: $year")
    }
}

Car.printInfo()

Anonymous objects

In Kotlin, anonymous objects are instances of anonymous classes. These classes are defined directly within an expression, without a separate class declaration.

An anonymous class is created using the object keyword followed by the declaration of the class or interface it implements, as well as its members.

open class Person(val name: String, val age: Int)

var hero = object : Person("Superman", 50){
    override fun toString(): String {
        return "I am $name and I am $age years old"
    }
}

println(hero) // I am Superman and I am 50 years old
if (hero is Person) { println("I am a person")}

Data Classes

In Kotlin, data classes are a special kind of class designed to hold data and provide some automatic functionality out of the box. They simplify the process of creating classes specifically for storing data.

data class Person(val name: String, val age: Int)

val p = Person("John", 25)
val p2 = p.copy(name =  "Jane", age = 26)

Sealed Classes

Sealed classes in Kotlin are a powerful feature that allow you to represent restricted class hierarchies. When you have a known set of subclasses, a sealed class can enforce that all subclasses are defined in the same file or within the same module. This is particularly useful for modeling state machines, result types, or other structures where the possible types are known and finite.

sealed class Result {
    data class Success(val data: String) : Result()
    data class Error(val data: String) : Result()
    data object Loading : Result()
}

fun handleResult(result: Result) {
    when (result) {
        is Result.Success -> println(result.data)
        is Result.Error -> println(result.data)
        is Result.Loading -> println("Loading...")
    }
}

fun main() {
    val success = Result.Success("Success message")
    val error = Result.Error("Error message")
    val loading = Result.Loading

    handleResult(success)
    handleResult(error)
    handleResult(loading)
}

Enum Classes

Enum classes in Kotlin are a way to define a set of named values. They are similar to Java’s enum classes, but with some additional features and syntax.

enum class Color(val name: String) {
    RED("Red"),
    GREEN("Green"),
    BLUE("Blue")
}

val color = Color.RED
println(color.name) // Red

Companion Objects

Companion objects are used to define static members for a class. They are declared inside a class and can access the private members of that class.

class Calc {
    companion object {
        fun add(a: Int, b: Int) = a + b
        fun sub(a: Int, b: Int) = a - b
        fun mul(a: Int, b: Int) = a * b
        fun div(a: Int, b: Int) = a / b
    }
}

fun main() {
    println(Calc.add(1, 2))
    println(Calc.sub(1, 2))
    println(Calc.mul(1, 2))
    println(Calc.div(1, 2))
}

Interfaces

interface Drivable {
    fun drive()
}

class Car : Drivable {
    override fun drive() {
        println("Driving a car")
    }
}

class Bike : Drivable {
    override fun drive() {
        println("Driving a bike")
    }
}

Default implementation:

interface Printable {
    fun print() {
        println("Printing from Printable")
    }
}

class Document : Printable {
    override fun print() {
        println("Printing from Document")
    }
}

// Uses the default implementation of print()
class Image : Printable

Class Inheritance

open class Animal {
    open fun sound() {
        println("I am an animal")
    }
}

class Dog : Animal() {
    override fun sound() {
        println("Woof!")
    }
}

class Cat : Animal() {
    override fun sound() {
        println("Meow!")
    }
}

Contructor and initialization:

open class Animal(val name: String) {
    open fun makeSound() {
        println("$name makes a sound")
    }
}

class Dog(name: String) : Animal(name) {
    override fun makeSound() {
        println("$name barks")
    }
}

Visibility modifiers

  • public is the default visibility if no modifier is specified.

  • private restricts access to within the class for class members and within the file for top-level declarations.

  • protected visibility is similar to private but allows access in subclasses. It cannot be used for top-level declarations.

  • internal restricts access to within the same module, making it useful for encapsulating implementation details that are not meant to be exposed outside the module.

Example:

class MyClass {
    val myPublicProperty = "I am public"
    private val myPrivateProperty = "I am private"
    protected val myProtectedProperty = "I am protected"
    internal val myInternalProperty = "I am internal"
}

Object extension

In Kotlin, object extensions allow you to add new functionality to existing classes without modifying their source code.

class Calc {}
fun Calc.add(a: Int, b: Int): Int = a + b
fun Calc.sub(a: Int, b: Int): Int = a - b
fun Calc.mul(a: Int, b: Int): Int = a * b
fun Calc.div(a: Int, b: Int): Int = a / b

val calc = Calc()
println(calc.add(1, 2))
println(calc.sub(1, 2))
println(calc.mul(1, 2))
println(calc.div(1, 2))

Funcional Interfaces (SAM)

An interface with only one abstract method is called a functional interface, or a Single Abstract Method (SAM) interface. The functional interface can have several non-abstract members but only one abstract member.

fun interface Calculable {
    fun action(x: Double, y: Double): Double
}

fun calc(a: Double, b: Double, calc: Calculable): Double {
    return calc.action(a, b)
}

fun main() {
    var add = calc(2.0, 3.0) { x, y -> x + y }
    var sub = calc(2.0, 3.0) { x, y -> x - y }
    var mul = calc(2.0, 3.0) { x, y -> x * y }
    var div = calc(2.0, 3.0) { x, y -> x / y }
}

Coroutines - basic concepts

Kotlin Coroutines is a library that allows you to write asynchronous code in a sequential way. It provides a set of suspending functions that can be called from within a coroutine.

launch is used to start a new coroutine. It is a fire-and-forget mechanism, meaning the coroutine is started and runs concurrently with the rest of the code.

import kotlinx.coroutines.*

fun main() {
    GlobalScope.launch {
        // This coroutine runs in the background
        delay(1000L)
        println("Hello from coroutine!")
    }
    println("Hello from main thread!")
    Thread.sleep(2000L) // Keep the main thread alive
}

runBlocking creates a coroutine that blocks the current thread until its completion. It’s often used in main functions and tests to bridge the gap between blocking and non-blocking code.

import kotlinx.coroutines.*

fun main() = runBlocking {
    // This coroutine runs in the main thread and blocks it
    launch {
        delay(1000L)
        println("Hello from coroutine!")
    }
    println("Hello from main thread!")
}

delay is a suspending function that pauses the coroutine without blocking the underlying thread. It is used to mimic non-blocking delays.

import kotlinx.coroutines.*

fun main() = runBlocking {
    println("Start")
    delay(1000L)
    println("End after 1 second delay")
}

Coroutines - suspending functions

A suspending function is a function that can suspend the execution of a coroutine without blocking the thread. It is marked with the suspend keyword.

import kotlinx.coroutines.*

suspend fun fetchData(): String {
    delay(1000L) // Simulate long-running operation
    return "Data fetched"
}

fun main() = runBlocking {
    val data = fetchData() // Call the suspending function
    println(data) // Print the result
}

Coroutines - scopes

GlobalScope

  • Launches coroutines that are not bound to any specific scope.
  • Use with caution as it creates global coroutines which live until the application runs.
import kotlinx.coroutines.*

fun main() {
    GlobalScope.launch {
        delay(1000L)
        println("Hello from GlobalScope")
    }
    Thread.sleep(2000L) // Keep the main thread alive
}

CoroutineScope

  • Defines a scope for new coroutines and manages their lifecycle.
  • Use it to create structured concurrency.
import kotlinx.coroutines.*

fun main() = runBlocking {
    val customScope = CoroutineScope(Dispatchers.Default)
    customScope.launch {
        delay(1000L)
        println("Hello from CoroutineScope")
    }
    // Wait for the coroutine to finish
    delay(2000L)
}

runBlocking

  • Creates a coroutine that blocks the current thread until its completion.
  • Typically used in main functions and tests.
import kotlinx.coroutines.*

fun main() = runBlocking {
    launch {
        delay(1000L)
        println("Hello from runBlocking")
    }
    println("Hello from main thread")
}

Error Handling

try {
    var result = 10 / 0
} catch (e: Exception) {
    println("Error: ${e.message}")
} finally {
    println("Finally")
}

Custom error handling:

class MyException(message: String) : Exception(message)

fun divide(a: Int, b: Int): Int {
    if (b == 0) {
        throw MyException("Division by zero")
    }
    return a / b
}

fun main() {
    try {
        val result = divide(10, 0)
        println(result)
    } catch (e: MyException) {
        println(e.message)
    } finally {
        println("Finally")
    }
}

Let Expression

object.let { it -> // Code to execute }

//common use
val name: String? = "Kotlin"
name?.let {
    println("The length of the name is ${it.length}")
}

//temporary variable
val (a, b) = "Kotlin".let { it.length to it }

//chaining Operations
val result = "Kotlin".let { it.toUpperCase() }.let { it.reversed() }
println(result) //NILTOK

//chaining transformations
val transformed = "123"
                .let { it.toInt() }
                .let { it * 2 }
                .let { it.toString() }
println(result) //246

Pair and Triple

Pair: is a data class that contains two values of the same type.

val pair = Pair(1, "a")
val (a, b) = pair

println(a) //1
println(b) //a

//short declaration using `to`
val pair2 = 1 to "a"
val (a, b) = pair2

println(a) //1
println(b) //a

//using pair in a function return
fun getPair(): Pair<Int, String> {
    return 1 to "a"
}

val pair3 = getPair()
val (a, b) = pair3

println(a) //1
println(b) //a

Triple: is a data class that contains three values of the same type.

val triple = Triple(1, "a", true)
val (a, b, c) = triple

println(a) //1
println(b) //a
println(c) //true

Destructuring declarations

A destructuring declaration allows you to assign multiple variables at once from a single object. This is typically done using the componentN() functions that are automatically generated for data classes and can be manually defined for custom classes.

data class Person(val name: String, val age: Int, val email: String)

fun main() {
    val person = Person("Alice", 30, "alice@example.com")

    // Destructuring declaration
    val (name, age, email) = person

    println("Name: $name")
    println("Age: $age")
    println("Email: $email")
}

Custom destructuring declaration:

You can define your own componentN() functions to enable destructuring for custom classes.

class Point(val x: Int, val y: Int) {
    operator fun component1() = x
    operator fun component2() = y
}

fun main() {
    val point = Point(10, 20)

    // Destructuring declaration
    val (x, y) = point

    println("x: $x, y: $y")
}

Delegation

Delegation is a feature in Kotlin that allows you to delegate the implementation of a function or property to another object. This can be useful when you want to provide a default implementation for a function or property, or when you want to share the implementation between multiple objects.

Class delegation:

interface Base {
    fun add(a: Int, b: Int): Int
    fun sub(a: Int, b: Int): Int
    fun mul(a: Int, b: Int): Int
    fun div(a: Int, b: Int): Int
}

class BaseImpl : Base {
    override fun add(a: Int, b: Int): Int { return a + b }
    override fun sub(a: Int, b: Int): Int { return a - b }
    override fun mul(a: Int, b: Int): Int { return a * b }
    override fun div(a: Int, b: Int): Int { return a / b }
}

class Calc : Base by BaseImpl(){
    override fun add(a: Int, b: Int): Int{
        println("add from calc")
        return a + b
    }
}

class Math : Base by Calc() {
    override fun sub(a: Int, b: Int): Int {
        println("sub from math")
        return a - b
    }
}

fun main() {
    val math = Math()
    println(math.add(1, 2))
    println(math.sub(1, 2))
    println(math.mul(1, 2))
    println(math.div(1, 2))
}

Property delegation:

class Person(){
    //lazy delegate
    val name: String by lazy {
        println("Lazy initializing")
        "John"
    }

    //observable delegate
    var obs: String by Delegates.observable("Initial value"){ _, old, new ->
        println("$old to $new")
    }

    //veto delegate called age
    var age: Int by Delegates.vetoable(0) { _, old, new ->
        if (new < 0) throw IllegalArgumentException("Age must be positive")
        println("$old to $new")
        true
    }
}

fun main() {
    val p = Person()
    println(p.name)
    println(p.obs)
    p.obs = "THE NEW VALUE!!!"
    println(p.obs)

    //Lazy initializing
    //John
    //Initial value
    //Initial value to THE NEW VALUE!!!
    //THE NEW VALUE!!!
}

Keywords

Hard Keywords:

KeywordDescription
asUsed for type casting.
as?Safe type casting that returns null if the cast is not possible.
breakTerminates the nearest enclosing loop.
classDeclares a class.
continueProceeds to the next iteration of the nearest enclosing loop.
doUsed in do-while loops.
elseUsed with if for conditional branching.
falseBoolean literal.
forUsed for loops.
funDeclares a function.
ifConditional branching.
inChecks for membership in a range or collection.
!inChecks for non-membership in a range or collection.
interfaceDeclares an interface.
isChecks for type compatibility.
!isChecks for type incompatibility.
nullRepresents a null reference.
objectDeclares an object.
packageSpecifies the package for the file.
returnReturns from a function.
superRefers to the superclass implementation.
thisRefers to the current instance.
throwThrows an exception.
trueBoolean literal.
tryStarts a try-catch block.
typealiasDeclares a type alias.
valDeclares a read-only variable.
varDeclares a mutable variable.
whenConditional branching, similar to a switch statement in other languages.
whileStarts a while loop.

Soft Keywords:

KeywordDescription
byUsed for delegation.
catchUsed in try-catch blocks to catch exceptions.
constructorDeclares a constructor.
delegateUsed to specify a property delegate.
dynamicMarks a member as dynamic, typically used for dynamic languages interop.
fieldReferences the backing field of a property.
fileUsed in file annotations.
finallyUsed in try-catch blocks to execute code regardless of whether an exception is thrown.
getDefines a getter for a property.
importImports a package, class, or function.
initDeclares an initializer block.
paramUsed in annotations to reference constructor parameters.
propertyUsed in annotations to reference properties.
receiverUsed in annotations to reference the receiver parameter.
setDefines a setter for a property.
setparamUsed in annotations to reference setter parameters.
whereSpecifies type constraints in generic declarations.

Modifier Keywords:

KeywordDescription
abstractMarks a class or member as abstract, meaning it cannot be instantiated or must be overridden.
annotationDeclares an annotation class.
companionDeclares a companion object inside a class.
constMarks a property as a compile-time constant.
crossinlineEnsures that a lambda parameter is not inlined at the call site.
dataDeclares a data class, which automatically generates useful methods like equals and hashCode.
enumDeclares an enumeration class.
expectDeclares expected declarations in multiplatform projects.
externalMarks a function or property as implemented outside of Kotlin (e.g., in C/C++).
finalProhibits a class or member from being overridden.
infixAllows a function to be called using infix notation.
inlineRequests the compiler to inline the function to improve performance.
innerAllows an inner class to access members of its outer class.
internalRestricts visibility to the same module.
lateinitDefers property initialization.
noinlineEnsures that a lambda parameter is not inlined.
openAllows a class or member to be overridden.
operatorMarks a function as an operator overload.
outIndicates that a type parameter is covariant.
overrideIndicates that a member is overriding a member in a superclass.
privateRestricts visibility to the same class.
protectedRestricts visibility to the same class and subclasses.
publicSpecifies that the member is visible everywhere (default visibility).
reifiedAllows a type parameter to be accessed at runtime.
sealedDeclares a sealed class, which restricts subclassing to the same file.
suspendMarks a function as suspending (usable in coroutines).
tailrecMarks a function as tail-recursive to optimize recursion.
varargAllows a function to accept a variable number of arguments.

Specified identifiers:

IdentifierDescription
fieldRefers to the backing field of a property. Used inside custom getter and setter functions.
itThe default name for the single parameter in a lambda expression if no explicit name is provided.