Kotlin Language Elements
Language elements are the core building blocks and features provided by Kotlin that are used to write programs. These are the fundamental syntax and constructs for defining data, behavior, and program flow.
Kotlin's key language elements include:
Data Types
Properties and Variables
Comments
Functions
Classes
Constructors
Objects and Instances
Interfaces
Control Flow
Kotlin Packages and Imports
Data Types in Kotlin
Kotlin has a small set of built-in data types. At runtime on the JVM, most (but not all) of them are mapped to Java primitive types for maximum performance, but in Kotlin source code they all behave like regular objects (methods can be called on a number, etc.).
Basic Value Types
- Numbers —
Byte,Short,Int,Long,Float,Double
→ compiled to Java primitives (byte,short,int,long,float,double) when non-nullable - Boolean —
Boolean
→ compiled to Javabooleanif non-nullable (Boolean), otherwise tojava.lang.Booleanif nullable (Boolean?) - Character —
Char
→ compiled to Javacharif non-nullable (Char), otherwise tojava.lang.Characterif nullable (Char?)
Reference Types
- String —
String
→ immutable, heap-allocated reference type (compiles tojava.lang.String) - Arrays
Array<T>— generic object array
→ compiles tojava.lang.Object[]
- Primitive arrays —
IntArray,LongArray,DoubleArray, etc.
→ compile directly to Java primitive arrays (int[],long[], …)
- Other common reference types (examples)
→ Classes & interfaces:List,Map,Set, custom classes — reference types by design
Type System (Structural) Types: Any, Unit, and Nothing
Any— root of the Kotlin type hierarchy for all non-nullable typesUnit— type with a single value, used as a return type when no meaningful result is producedNothing— type with no values; used to mark code paths that never return normally
Variable & Property Declaration
Variables
In Kotlin, variables are declared using var (mutable) or val (immutable reference).
- Local variables:
- Declared inside functions, blocks, or loops — simple storage with no getters/setters or backing fields.
- Function parameters are also local variables: they act as immutable (
val) variables within the function body.
Properties
- Class-level and top-level properties: Declared at class level or top level. They have backing fields (when needed), default or custom getters/setters, and support visibility modifiers. Properties are part of the class’s (or file’s) API.
- Top-level declarations: Declared outside any class. Despite being "global" in scope, Kotlin treats them as properties (implemented as static fields with getters/setters in the generated JVM bytecode).
Variable or Property?
Every property is a variable, but not every variable is a property — the difference depends on where it is declared. Only class-level and top-level declarations qualify as properties. Local variables (including function parameters) are never properties.
Declaration Keywords
var— mutable (value can be reassigned)val— immutable reference (likefinalin Java)const val— compile-time constant (restricted to primitives andString; must be top-level or inside anobject/companion object)
// Mutable variable (reassignable)
var count = 10
var name: String = "Kotlin"
count = 20 // OK
// name = null // Error if not nullable
// Immutable reference (read-only)
val id = 1001
val language = "Kotlin"
// id = 2000 // Error: val cannot be reassigned
// Compile-time constant
const val VERSION = "1.9.20"
const val MAX_USERS = 10_000
// Type inference (preferred in most cases)
val isActive = true
val price = 99.9 // inferred as Double
val letter: Char = 'A'
// Nullable types
var email: String? = null
email = "user@example.com"
// Common collections (all immutable by default)
val numbers = listOf(1, 2, 3, 4, 5)
val fruits = setOf("apple", "banana", "orange")
val scores = mapOf(1 to "Gold", 2 to "Silver", 3 to "Bronze")
// Mutable collections
var mutableList = mutableListOf("Jan", "Feb")
mutableList.add("Mar")
// Ranges
val oneToTen = 1..10
val aToZ = 'a'..'z'
Kotlin uses type inference heavily, so rarely need to write the type if it can be inferred.
Lateinit (for var only, non-primitive, non-null):
lateinit var database: DatabaseConnection // initialized later, before first use
Lazy delegation (computed once, on first access):
val heavyConfig: Config by lazy {
println("Loading config...")
loadConfigFromFile()
}
In Kotlin, there is no need to put a semicolon ( ; ) at the end of each line — it is completely optional, just like in JavaScript, Python, or Swift.
These two pieces of code work exactly the same:
val name = "Alice"
val age = 25
println("Hola")
val name = "Alice"; val age = 25; println("Hola")
Only write a semicolon if two or more statements are put on the same line (not common as it makes code harder to read):
val x = 5; val y = 10 // semicolon required here
Comments
// This is a single line Kotlin comment
/* This is
* multi-line Kotlin comment
*/
/**
* This is a KDoc comment - used for documentation
* @param name the name to greet
* @return greeting string
*/
//:: This is a special comment often used for IDE directives
// TODO: Mark something that needs to be done later
// FIXME: Highlight problematic code that needs correction
// !! This comment draws attention (use sparingly)
/*
* Multi-line comments can also
* be written without asterisks
*/
// Deprecated: Indicates outdated code
// (Often used with @Deprecated annotation)
Functions
Functions - Functions in Kotlin are declared using the fun keyword and provide reusable blocks of code.
Main Function - The entry point for a Kotlin program.
fun main() {
println("Kotlin Tutorial at www.zyasin.com!")
}
Top-Level Function - Defined directly in files, not tied to any class.
fun greet(name: String): String {
return "Hello, $name!"
}
Function Types:
1. Basic Function - With parameters and return type.
fun add(a: Int, b: Int): Int {
return a + b
}
2. Single-Expression Function - Shorter syntax when function returns a single expression.
fun multiply(a: Int, b: Int) = a * b
3. Default Arguments - Parameters with default values.
fun greet(name: String = "Guest") {
println("Welcome, $name!")
}
4. Named Arguments - Call functions with parameter names.
fun createUser(name: String, age: Int, isAdmin: Boolean = false) { ... }
// Usage:
createUser(age = 25, name = "Alice")
5. Extension Functions - Add functionality to existing classes.
fun String.addExclamation() = "$this!"
// Usage:
"Hello".addExclamation() // "Hello!"
6. Higher-Order Functions - Functions that take other functions as parameters.
fun calculate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
return operation(a, b)
}
// Usage:
calculate(5, 3) { x, y -> x + y }
7. Lambda Expressions - Anonymous functions.
val sum = { a: Int, b: Int -> a + b }
val result = sum(2, 3) // 5
// Function type declaration
val greet: () -> Unit = { println("Hello!") }
val add: (Int, Int) -> Int = { a, b -> a + b }
8. Infix Functions - Called without dot and parentheses.
infix fun Int.times(str: String) = str.repeat(this)
// Usage:
3 times "Hi" // "HiHiHi"
9. Tail Recursive Functions - Optimized recursion.
tailrec fun factorial(n: Int, acc: Int = 1): Int {
return if (n == 1) acc else factorial(n - 1, acc * n)
}
10. Operator Overloading - Define behavior for operators.
operator fun Point.plus(other: Point) = Point(x + other.x, y + other.y)
// Usage:
val p1 = Point(1, 2)
val p2 = Point(3, 4)
val p3 = p1 + p2 // Point(4, 6)
Classes
To define a Kotlin class use class keyword, followed by class name.
class ClassName {
// Declare properties (using keywords val or var.)
// Declare functions (using keyword fun.)
}
By default, Kotlin classes are final and cannot be extended unless explicitly marked with the open keyword.
Kotlin provides visibility modifiers like private, protected, internal, and public to control the visibility of class members. By default, class members are public.
Kotlin supports various types of classes, each serving a different purpose:
- Regular classes: Standard classes for objects, behavior, and state.
- Inner/nested classes: Classes inside classes; inner accesses outer instance.
- Abstract classes: Cannot be instantiated or final; open by default for inheritance; can have abstract + implemented members.
- Data classes: Auto-generate equals(), hashCode(), toString(), copy().
- Sealed classes: Restricted hierarchies; all subclasses in same file.
- Enum classes: Fixed set of constant objects.
- Object declarations: Singletons (one instance only).
- Companion objects: “Static” members inside a class.
Constructors
For proper object initialization, a Kotlin class can have Primary and Secondary constructors.
Primary Constructor:It is the main way to initialize the properties of a class, defined as part of the class header and responsible for initializing the properties when an object of class is created. To declare a primary constructor keyword constructor can be optionally added just after the class name. The primary constructor defined with the class header do not contain any code. Initialization code is placed within initializer blocks, inside the class body prefixed with the init keyword.
class ClassName(parameters) {
//declare properties (using key words val or var.)
// initializer blocks
}
Secondary Constructor:
A class can have zero, one, or multiple secondary constructors. These are defined using the constructor keyword.
Secondary constructors allow additional ways to initialize a class and can include custom logic. With multiple constructors,
each initializes the class in a different way. The compiler determines which constructor to invoke based on the arguments provided.
class Student(val studentId: Int, val firstName: String, val lastName: String) {
val fullName: String
init {
fullName = "$firstName $lastName"
}
// Secondary costructor
constructor(firstName: String, lastName: String) : this(0, firstName, lastName)
}
Here the Student class has a secondary constructor that takes only firstName and lastName parameters. The secondary constructor delegates to the primary constructor using the this keyword and provides a default value of 0 for the studentId parameter. This allows to create Student objects without specifying a student ID.
Objects and Instances
In Kotlin, a class describes what something can have and do.
An object
(or instance) is one real, usable thing created from that class.
To create an object, simply call the class name like a normal function — no new keyword is needed:
// A simple class
class Car(val brand: String, var speed: Int)
// Creating real objects (instances)
val car1 = Car("Toyota", 0)
val car2 = Car("Tesla", 100)
var myCar = Car("Honda", 60)
// Using the objects with dot notation
println(car1.brand) // → Toyota
myCar.speed = 80 // change speed (var = mutable)
car2.speed++ // increase by 1
// If the class has a function:
myCar.startEngine() // calls the function
Important to note:
- No
newkeyword — just writeClassName(...) - Use a dot
.to read or change properties and to call functions
→objectName.propertyorobjectName.function() - Properties declared with
valin the constructor cannot be changed later
Properties declared withvarcan be changed - Every object has its own separate values
Example: Many different Car objects can have many different speeds, even though they all come from the same Car class.
Interfaces
In Kotlin, interfaces can contain method implementation as well as abstract methods and properties declaration. An interface needs to be implemented by a class in order for any use. In Kotlin interfaces are not implicitly final. Interfaces can be implemented by any class without any restrictions. However, interface can be forced to be final by using "final" key word before them, which then prevent other classes from implementing it or inheriting from it, however it is extremely rare cases where such need can arise.
interface KotlinInterface {
var someString: String // abstract property
fun someFunction() // abstract method
fun string() = "Some string" // method with default implementation
}
To implement above interface in a class:
class ImplementKotlinInterface : interface KotlinInterface {
override var someString: String = "some string" // property declared in interface implemented
override fun someFunction() = "display string outpt" // function declared in interface implemented
override fun string() = "Some string text overriden" // text in method of interfce overridden
}
Control Flow
val x = 14
if (x > 14) {
println("x is greater than 14")
} else if (x == 14) {
println("x is equal to 14")
} else {
println("x is less than 14")
}
val x = 3
when (x) {
1 -> println("x is 1")
2 -> println("x is 2")
else -> println("x is neither 1 nor 2")
}
for (i in 1..5) {
println(i)
}
fun isPositive(number: Int): Boolean {
if (number > 0) {
return true
} else {
return false
}
}
for (i in 1..10) {
if (i == 5) {
break
}
println(i)
}
for (i in 1..5) {
if (i == 3) {
continue
}
println(i)
}
Kotlin Packages and Imports
In Kotlin, code is organized as packages, which also helps prevent naming conflicts. A package is declared at the top of a Kotlin file using the package keyword. If no package is specified,
the file belongs to the default package.
Kotlin also provides import statements to bring external classes, functions, or objects into the current file. The import keyword allows you to access elements from other packages
without needing to use their fully qualified names.
package com.example.app
import kotlin.math.PI
import kotlin.math.sqrt
fun main() {
println("The square root of 25 is: " + sqrt(25.0))
println("The value of PI is: $PI")
}
Here, the package name is com.example.app and import functions from kotlin.math.
This allows to use PI and sqrt() directly without referring to their full package names.