Kotlin language specification
- Kotlin/Core
- Introduction
- Syntax and grammar
- Type system
- Glossary
- Introduction
- Type kinds
- Type contexts and scopes
- Subtyping
- Upper and lower bounds
- Type approximation
- Type decaying
- References
- Built-in types and their semantics
kotlin.Anykotlin.Nothingkotlin.Unitkotlin.Boolean- Built-in integer types
- Built-in floating point arithmetic types
kotlin.Charkotlin.Stringkotlin.Enum- Built-in array types
- Iterator types
kotlin.Throwablekotlin.Comparablekotlin.Function- Built-in annotation types
- Reflection support builtin types
- Declarations
- Glossary
- Introduction
- Classifier declaration
- Function declaration
- Property declaration
- Type alias
- Declarations with type parameters
- Declaration visibility
- Inheritance
- Scopes and identifiers
- Statements
- Expressions
- Glossary
- Introduction
- Constant literals
- Constant expressions
- String interpolation expressions
- Try-expressions
- Conditional expressions
- When expressions
- Logical disjunction expressions
- Logical conjunction expressions
- Equality expressions
- Comparison expressions
- Type-checking and containment-checking expressions
- Elvis operator expressions
- Range expressions
- Additive expressions
- Multiplicative expressions
- Cast expressions
- Prefix expressions
- Postfix operator expressions
- Not-null assertion expressions
- Indexing expressions
- Call and property access expressions
- Function literals
- Object literals
- This-expressions
- Super-forms
- Jump expressions
- Operator overloading
- Packages and imports
- Overload resolution
- Glossary
- Introduction
- Basics
- Building the overload candidate set
- Determining function applicability for a specific call
- Choosing the most specific candidate from the overload candidate set
- Resolving property access
- Resolving callable references
- Type inference and overload resolution
- Conflicting overloads
- Control- and data-flow analysis
- Kotlin type constraints
- Type inference
- Runtime type information
- Exceptions
- Annotations
- Annotation values
- Annotation retention
- Annotation targets
- Annotation declarations
- Built-in annotations
kotlin.annotation.Retentionkotlin.annotation.Targetkotlin.annotation.Repeatablekotlin.RequiresOptIn/kotlin.OptInkotlin.Deprecated/kotlin.ReplaceWithkotlin.Suppresskotlin.SinceKotlinkotlin.UnsafeVariancekotlin.DslMarkerkotlin.PublishedApikotlin.BuilderInferencekotlin.RestrictSuspensionkotlin.OverloadResolutionByLambdaReturnType
- Asynchronous programming with coroutines
- Concurrency
Statements
Kotlin does not explicitly distinguish between statements, expressions and declarations, i.e., expressions and declarations can be used in statement positions. This section focuses only on those statements that are not expressions or declarations. For information on those parts of Kotlin, please refer to the Expressions and Declarations sections of the specification.
Example: Kotlin supports using conditionals both as expressions and as statements. As their use as expressions is more general, detailed information about conditionals is available in the Expressions section of the specification.
Assignments
- assignmentAndOperator:
'+='
|'-='
|'*='
|'/='
|'%='
An assignment is a statement that writes a new value to some program entity, denoted by its left-hand side. Both left-hand and right-hand sides of an assignment must be expressions, more so, there are several restrictions for the expression on the left-hand side.
For an expression to be assignable, i.e. be allowed to occur on the left-hand side of an assignment, it must be one of the following:
- An identifier referring to a mutable property;
- A navigation expression referring to a mutable property. If this navigation operator is the safe navigation operator, this introduces a special case of safe assignment;
- An indexing expression.
Note: Kotlin assignments are not expressions and cannot be used as such.
Simple assignments
A simple assignment is an assignment which uses the assign operator =. If the left-hand side of an assignment refers to a mutable property, a value of that property is changed when an assignment is evaluated, using the following rules (applied in order).
- If a property has a setter (including delegated properties), it is called using the right-hand side expression as its argument;
- Otherwise, if a property is a mutable property, its value is changed to the evaluation result of the right-hand side expression.
If the left-hand side of an assignment is an indexing expression, the whole statement is treated as an overloaded operator with the following expansion:
is the same as calling
where set is a suitable operator function.
Operator assignments
An operator assignment is a combined-form assignment which involves one of the following operators: +=, -=, *=, /=, %=. All of these operators are overloadable operator functions with the following expansions (applied in order):
-
is exactly the same as one of the following:
-
if a suitable
plusAssignoperator function exists and is available; -
if a suitable
plusoperator function exists and is available.
-
if a suitable
-
is exactly the same as one of the following:
-
if a suitable
minusAssignoperator function exists and is available; -
if a suitable
minusoperator function exists and is available.
-
if a suitable
-
is exactly the same as one of the following:
-
if a suitable
timesAssignoperator function exists and is available; -
if a suitable
timesoperator function exists and is available.
-
if a suitable
-
is exactly the same as one of the following:
-
if a suitable
divAssignoperator function exists and is available; -
if a suitable
divoperator function exists and is available;
-
if a suitable
-
is exactly the same as one of the following:
-
if a suitable
remAssignoperator function exists and is available; -
if a suitable
remoperator function exists and is available.
-
if a suitable
Note: before Kotlin version 1.3, there were additional overloadable functions for
%calledmod/modAssign
After the expansion, the resulting function call expression or simple assignment is processed according to their corresponding rules, and overload resolution and type checking are performed. If both expansion variants result in correctly resolved and inferred code, this should be reported as an operator overloading ambiguity. If only one of the expansion variants can be resolved correctly, this variant is picked as the correct one. If neither of variants result in correct code, the operator calls must be reported as unresolved.
Example: consider the following compound operator statement:
x[y] += z. The corresponding expansion variants arex.get(y).plusAssign(z)andx.set(x.get(y).plus(z))according to expansion rules for corresponding operators. If, for example, the call tosetin the second variant results in resolution or inference error, the whole corresponding expansion is deemed unresolved and the first variant is picked if applicable.
Note: although for most real-world use cases operators
++and--are similar to operator assignments, in Kotlin they are expressions and are described in the corresponding section of this specification.
Safe assignments
If the left-hand side of an assignment involves a safe-navigation operator, it is treated as a special case of safe assignment. Safe assignments are expanded similar to safe navigation operator expressions:
a?.cis exactly the same aswhen(val $tmp = a) { null -> null else -> { $tmp.c } }For any right-hand combinations of operators present in
c, which are expanded further, as usual.
Example: The assignment
is expanded to
when(val $tmp = x) { null -> null else -> { $tmp.y[0] = z } }which, according to expansion rules for indexing assignments is, in turn, expanded to
when(val $tmp = x) { null -> null else -> { $tmp.y.set(0, z) } }
Loop statements
Loop statements describe an evaluation of a certain number of statements repeatedly until a loop exit condition applies.
Loops are closely related to the semantics of jump expressions, as these expressions, namely break and continue, are only allowed in a body of a loop. Please refer to the corresponding sections for details.
While-loop statements
A while-loop statement is similar to an if expression in that it also has a condition expression and a body consisting of zero or more statements. While-loop statement evaluating its body repeatedly for as long as its condition expression evaluates to true or a jump expression is evaluated to finish the loop.
Note: this also means that the condition expression is evaluated before every evaluation of the body, including the first one.
The while-loop condition expression must be a subtype of kotlin.Boolean.
Do-while-loop statements
A do-while-loop statement, similarly to a while-loop statement, also describes a loop, with the following differences. First, it has a different syntax. Second, it evaluates the loop condition expression after evaluating the loop body.
Note: this also means that the body is always evaluated at least once.
The do-while-loop condition expression must be a subtype of kotlin.Boolean.
For-loop statements
Note: unlike most other languages, Kotlin does not have a free-form condition-based for loops. The only form of a for-loop available in Kotlin is the “foreach” loop, which iterates over lists, arrays and other data structures.
A for-loop statement is a special kind of loop statement used to iterate over some data structure viewed as an iterable collection of elements. A for-loop statement consists of a loop body, a container expression and an iteration variable declaration.
The for-loop is actually an overloadable syntax form with the following expansion:
for(VarDecl in C) Body is the same as
when(val $iterator = C.iterator()) {
else -> while ($iterator.hasNext()) {
val VarDecl = __iterator.next()
<... all the statements from Body>
}
}where iterator, hasNext, next are all suitable operator functions available in the current scope. VarDecl here may be a variable name or a set of variable names as per destructuring variable declarations.
Note: the expansion is hygienic, i.e., the generated iterator variable never clashes with any other variable in the program and cannot be accessed outside the expansion.
Code blocks
A code block is a sequence of zero or more statements between curly braces separated by newlines or/and semicolons. Evaluating a code block means evaluating all its statements in the order they appear inside of it.
Note: Kotlin does not support code blocks as statements; a curly-braces code block in a statement position is a lambda literal.
A last expression of a code block is the last statement in it (if any) if and only if this statement is also an expression. A code block is said to contain no last expression if it does not contain any statements or its last statement is not an expression (e.g., it is an assignment, a loop or a declaration).
Informally: you may consider the case of a missing last expression as if a synthetic last expression with no runtime semantics and type
kotlin.Unitis introduced in its place.
A control structure body is either a single statement or a code block. A last expression of a control structure body is either the last expression of a code block (if is a code block) or the single expression itself (if is an expression). If a control structure body is not a code block or an expression, it has no last expression.
Note: this is equivalent to wrapping the single expression in a new synthetic code block.
In some contexts, a control structure body is expected to have a value and/or a type. The value of a control structure body is:
- the value of its last expression if it exists;
- the singleton
kotlin.Unitobject otherwise.
The type of a control structure body is the type of its value.
Coercion to kotlin.Unit
When we expect the type of a control structure body to be kotlin.Unit, we relax the type checking requirements for its type by coercing it to kotlin.Unit. Specifically, we ignore the type mismatch between kotlin.Unit and the control structure body type.
Examples:
fun foo() { val a /* : () -> Unit */ = { if (true) 42 // CSB with no last expression // Type is defined to be `kotlin.Unit` } val b: () -> Unit = { if (true) 42 else -42 // CSB with last expression of type `kotlin.Int` // Type is expected to be `kotlin.Unit` // Coercion to kotlin.Unit applied } }