Post

Kotlin

Kotlin is a cross-platform, statically typed, general-purpose high-level programming language with type inference. Kotlin is designed to interoperate fully with Java.

Kotlin

Basics

var and val

The difference between var and val is that variables declared with the var keyword can be changed/modified, while val variables cannot.

Variable Type

Unlike many other programming languages, variables in Kotlin do not need to be declared with a specified type (like “String” for text or “Int” for numbers).

1
2
var name = "John"      
val birthyear = 1975

However, it is possible to specify the type:

1
2
var name: String = "John" 
val birthyear: Int = 1975

Type Conversion

Type conversion is when you convert the value of one data type to another type. To convert a numeric data type to another type, you must use one of the following functions: toByte(), toShort(), toInt(), toLong(), toFloat(), toDouble() or toChar()

Example

1
2
val x: Int = 5
val y: Long = x.toLong()

Standard Input

1
2
var input = readLine()
print("You entered: $input")

String

A String in Kotlin is an object, which contain properties and functions that can perform certain operations on strings, by writing a dot character (.) after the specific string variable.

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
fun main() {
    val str = "Hello, Kotlin!"

    // Length
    println("Length: ${str.length}") // 13

    // Access characters
    println("Char at 1: ${str[1]}") // e

    // Case conversion
    println("Lowercase: ${str.lowercase()}") // hello, kotlin!
    println("Uppercase: ${str.uppercase()}") // HELLO, KOTLIN!

    // Trim
    val spaced = "  Kotlin  "
    println("Trim: '${spaced.trim()}'")
    println("Trim Start: '${spaced.trimStart()}'")
    println("Trim End: '${spaced.trimEnd()}'")

    // Substring
    println("Substring from 7: ${str.substring(7)}") // Kotlin!
    println("Substring 0-5: ${str.substring(0, 5)}") // Hello

    // Contains
    println("Contains 'Kot': ${str.contains("Kot")}") // true

    // StartsWith / EndsWith
    println("Starts with 'Hel': ${str.startsWith("Hel")}") // true
    println("Ends with '!': ${str.endsWith("!")}") // true

    // IndexOf / LastIndexOf
    println("Index of 'o': ${str.indexOf('o')}") // 4
    println("Last index of 'o': ${str.lastIndexOf('o')}") // 9

    // Replace / ReplaceFirst
    println("Replace: ${str.replace("Kotlin", "World")}") // Hello, World!
    println("Replace First: ${"one one one".replaceFirst("one", "1")}") // 1 one one

    // Split
    val parts = "a,b,c".split(",")
    println("Split: $parts") // [a, b, c]

    // JoinToString
    val list = listOf("a", "b", "c")
    println("JoinToString: ${list.joinToString(",")}") // a,b,c

    // Repeat
    println("Repeat: ${"Hi ".repeat(3)}") // Hi Hi Hi 

    // Reverse
    println("Reversed: ${"abc".reversed()}") // cba

    // isEmpty / isNotEmpty
    println("Is empty: ${"".isEmpty()}") // true
    println("Is not empty: ${"abc".isNotEmpty()}") // true

    // isBlank / isNotBlank
    println("Is blank: ${"   ".isBlank()}") // true
    println("Is not blank: ${" abc".isNotBlank()}") // true

    // compareTo
    println("CompareTo: ${"apple".compareTo("banana")}") // < 0

    // plus
    println("Plus: ${"Kotlin" + "Lang"}") // KotlinLang

    // padStart / padEnd
    println("Pad Start: ${"42".padStart(5, '0')}") // 00042
    println("Pad End: ${"42".padEnd(5, '*')}") // 42***

    // removePrefix / removeSuffix
    println("Remove Prefix: ${"unhappy".removePrefix("un")}") // happy
    println("Remove Suffix: ${"file.txt".removeSuffix(".txt")}") // file

    // drop / dropLast
    println("Drop 2: ${"Kotlin".drop(2)}") // tlin
    println("DropLast 2: ${"Kotlin".dropLast(2)}") // Kotl

    // take / takeLast
    println("Take 3: ${"Kotlin".take(3)}") // Kot
    println("TakeLast 3: ${"Kotlin".takeLast(3)}") // lin

    // Capitalize / Decapitalize (deprecated, use replaceFirstChar)
    println("Capitalize: ${"hello".replaceFirstChar { it.uppercase() }}") // Hello
    println("Decapitalize: ${"HELLO".replaceFirstChar { it.lowercase() }}") // hELLO

    // Format
    val name = "Tawhid"
    val age = 21
    println("Format: ${"My name is %s and Im %d years old".format(name, age)}")

    // toCharArray
    val chars = "Kotlin".toCharArray()
    println("ToCharArray: ${chars.joinToString()}") // K, o, t, l, i, n

    // Regex: matches, replace with Regex
    println("Matches \\d+: ${"1234".matches(Regex("\\d+"))}") // true
    println("Replace digits with #: ${"abc123".replace(Regex("\\d+"), "#")}") // abc#

    // Extra: also, let, run (higher-order extension functions)
    "Kotlin".also { println("Also: $it") }
    "Kotlin".let { println("Let: Length is ${it.length}") }
    "Kotlin".run {
        println("Run: First letter is ${this[0]}")
    }
}

If..Else Expressions

In Kotlin, you can also use if..else statements as expressions (assign a value to a variable and return it)

Example

1
2
3
4
5
6
val time = 20
val greeting = if (time < 18) {
  "Good day."
} else {
  "Good evening."
}

When

Instead of writing many if..else expressions, you can use the when expression, which is much easier to read.

1
2
3
4
5
6
7
8
9
10
11
val day = 4
val result = when (day) {
  1 -> "Monday"
  2 -> "Tuesday"
  3 -> "Wednesday"
  4 -> "Thursday"
  5 -> "Friday"
  6 -> "Saturday"
  7 -> "Sunday"
  else -> "Invalid day."
}

Array

Arrays are used to store multiple values in a single variable, instead of creating separate variables for each value.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
fun main() {
    // Create arrays
    val nums = arrayOf(1, 2, 3, 4, 5)
    val letters = arrayOf("a", "b", "c")

    // Size
    println("Size: ${nums.size}") // 5

    // Access and modify elements
    println("First element: ${nums[0]}")
    nums[0] = 10
    println("Modified first element: ${nums[0]}")

    // Get / Set
    println("Get at 2: ${nums.get(2)}")
    nums.set(2, 99)
    println("Set index 2 to 99: ${nums[2]}")

    // First / Last
    println("First: ${nums.first()}")
    println("Last: ${nums.last()}")

    // IndexOf / LastIndexOf
    println("Index of 99: ${nums.indexOf(99)}")
    println("Last index of 99: ${nums.lastIndexOf(99)}")

    // Contains / In
    println("Contains 4: ${nums.contains(4)}")
    println("3 in nums: ${3 in nums}") // false (we replaced 3 with 99)

    // isEmpty / isNotEmpty
    println("Is empty: ${nums.isEmpty()}")
    println("Is not empty: ${nums.isNotEmpty()}")

    // JoinToString
    println("JoinToString: ${nums.joinToString()}") // 10, 2, 99, 4, 5

    // forEach
    nums.forEach { println("Element: $it") }

    // forEachIndexed
    nums.forEachIndexed { index, value -> println("Index $index: $value") }

    // map
    val doubled = nums.map { it * 2 }
    println("Doubled: $doubled")

    // filter
    val even = nums.filter { it % 2 == 0 }
    println("Even numbers: $even")

    // any / all / none
    println("Any > 10: ${nums.any { it > 10 }}")
    println("All > 0: ${nums.all { it > 0 }}")
    println("None < 0: ${nums.none { it < 0 }}")

    // count
    println("Count > 5: ${nums.count { it > 5 }}")

    // find / findLast
    println("Find > 10: ${nums.find { it > 10 }}")
    println("FindLast > 10: ${nums.findLast { it > 10 }}")

    // sum / average / max / min
    println("Sum: ${nums.sum()}")
    println("Average: ${nums.average()}")
    println("Max: ${nums.maxOrNull()}")
    println("Min: ${nums.minOrNull()}")

    // take / takeLast
    println("Take 3: ${nums.take(3)}")
    println("TakeLast 3: ${nums.takeLast(3)}")

    // drop / dropLast
    println("Drop 2: ${nums.drop(2)}")
    println("DropLast 2: ${nums.dropLast(2)}")

    // reversed
    println("Reversed: ${nums.reversed()}")

    // sorted
    println("Sorted: ${nums.sorted()}")
    println("Sorted descending: ${nums.sortedDescending()}")

    // copyOf / copyOfRange
    val copied = nums.copyOf()
    println("CopyOf: ${copied.joinToString()}")
    val rangeCopy = nums.copyOfRange(1, 4)
    println("CopyOfRange 1-4: ${rangeCopy.joinToString()}")

    // fill
    val filled = Array(5) { 0 }
    filled.fill(7)
    println("Filled with 7: ${filled.joinToString()}")

    // withIndex
    for ((index, value) in nums.withIndex()) {
        println("WithIndex - $index: $value")
    }

    // toList
    val list = nums.toList()
    println("ToList: $list")

    // contentEquals / contentDeepEquals
    val arr1 = arrayOf(1, 2, 3)
    val arr2 = arrayOf(1, 2, 3)
    println("Content Equals: ${arr1.contentEquals(arr2)}")

    // contentToString
    println("ContentToString: ${arr1.contentToString()}")

    // multidimensional arrays
    val matrix = arrayOf(
        arrayOf(1, 2),
        arrayOf(3, 4)
    )
    println("2D Access: ${matrix[1][1]}") // 4

    // contentDeepToString
    println("Deep ToString: ${matrix.contentDeepToString()}")

    // distinct
    val dupArray = arrayOf(1, 2, 2, 3, 3, 3)
    println("Distinct: ${dupArray.distinct()}") // [1, 2, 3]

    // zip
    val a = arrayOf(1, 2, 3)
    val b = arrayOf("a", "b", "c")
    val zipped = a.zip(b)
    println("Zipped: $zipped") // [(1, a), (2, b), (3, c)]

    // unzip
    val (unzippedA, unzippedB) = zipped.unzip()
    println("Unzipped A: $unzippedA")
    println("Unzipped B: $unzippedB")
}

Ranges

With the for loop, you can also create ranges of values with ".."

1
2
3
for (nums in 5..15) {
  println(nums)
} 

The first and last value is included in the range.

Functions

A function is a block of code which only runs when it is called. You can pass data, known as parameters, into a function. Functions are used to perform certain actions, and they are also known as methods.

1
2
3
4
5
6
7
8
9
10
fun sum(x: Int, y: Int) = x + y

fun myFunction(x: Int, y: Int): Int {
  return (x + y)
}

fun main() {
  var result = myFunction(3, 5)
  println(result)  //8 (3 + 5) 
}

Null Safety

Kotlin’s null safety feature helps to eliminate the risk of NullPointerException by distinguishing between nullable and non-nullable types.

  • Nullable types (?)
  • Safe call operator (?.)
  • Elvis operator (?:)
  • Not-null assertion (!!)
  • let with safe calls

Examples

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
fun main() {
    // Nullable type
    val name: String? = getNameFromDatabase()

    // Safe call - won't crash if name is null
    println("Name length: ${name?.length}")

    // Elvis operator - provides default if null
    val displayName = name ?: "Guest"
    println("Hello, $displayName!")

    // Safe call with let
    name?.let {
        println("Name in uppercase: ${it.uppercase()}")
    }

    // Not-null assertion - throws exception if null
    try {
        val forcedName = name!!
        println("Forced name: $forcedName")
    } catch (e: KotlinNullPointerException) {
        println("Caught KotlinNullPointerException: name was null!")
    }
}

// This function simulates returning a nullable String
fun getNameFromDatabase(): String? {
    return null // or try returning "Tawhid"
}

Explanation

  • String? means it can be null.
  • name?.length safely accesses length if name is not null.
  • name ?: “Guest” means “use name if not null, else use ‘Guest’”.
  • name?.let { … } executes the block only if name is not null.
  • name!! forces access and will crash if name is null.

try/catch

This post is licensed under CC BY 4.0 by the author.