Kotlin

Kotlin is

  • cross-platform
  • statically typed (type of a variable is known at compile-time instead of at run-time)
  • with type inference (has the ability to automatically deduce the type of an expression at compile time)
  • general-purpose programming language
  • interoperates fully with Java
  • (JVM version) depends on the Java Class Library
  • can also compile to JavaScript or native code
  • as of 2019 Google's preferred language for Android app developers

Links

Kotlin in Wikipedia Kotlin in Wikipedia (German)

IDE

https://github.com/JetBrains/kotlin-eclipse

Functions

main function is the entry point of your code

fun main() {
    println("Hello World!")
}

Functions with parameters and a return value

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

One liner functions do not need a return type or {}

fun add(x: Int, y: Int) = x + y
fun maxOf(a: Int, b: Int) = if (a > b) a else b

Functions can have inner functions

fun main(args: Array<String>) {
    fun test() {
        println("Hello World!")
    }

    test()
}

Function that have functions as parameters

fun callWithInt(number: Int, fn: (Int) -> Int) {
    val result = fn(number)
    println(result)
}

Functions that return a function as a result

fun makeAdder(a: Int): (Int) -> Int {
    fun add(b: Int) = a + b
    return ::add
}

var number = 0
fun makeCounter(): () -> Int {
    fun counter() = ++number
    return ::counter
}

// examples how to use it
val add2 = makeAdder(2)
println(add2(5)) // gibt 7 aus

val counter = makeCounter()
println(counter()) // 1
println(counter()) // 2

// example calls
callWithInt(42, { x -> x * 2})
callWithInt(42, fun(x: Int) = x * 2)
// if function is last parameter it can be outside the () and it is the default name
callWithInt(42) { it * 2 }

Control flow

If

val a = 120

if (a == 100)
    println("X")
else if (a == 130)
    println("Y")
else
    println("Z")

When / Case / Switch

val x = 5
when (x) {
    1 -> print("x == 1")
    2 -> print("x == 2")
    else -> {
        print("Not 1 or 2")
    }
}

Variables

val x = 5
var y = 0
val s = "World"
var obj: Any
...
if (obj is String) {
   // obj is automatically cast to String in this branch
   return obj.length
}

Templates

var a = 1
val s1 = "a is $a"

a = 2
val s2 = "${s1.replace("is", "was")}, but now is $a"

Null

Normal variables can not be null

var a: Int = 123
a = null // Compiler error

Add a ? to the type makes them nullable

var b: Int? = 456
b = null // ok

But then you need to check that they are not null when you use something from them. That can be annoying.

Imagine you have

foo.bar.myvalue

and want to get myvalue. But foo, foo.bar and foo.bar.myvalue can be null. You need to write something like this:

val myvalue: String?
if (foo == null || foo.bar == null)
    myvalue = null
else
    myvalue = foo.bar.myvalue

Short version in Kotlin:

val myvalue = foo?.bar?.myvalue

Get the value of foo or if null the else value

val bar = foo ?: "is empty"

Return value of function can be null

fun parseInt(str: String): Int? {

Collections

If you have a function that expects parameters and you have an Array, you can use *myArray to automatically use each of the entries of the Array as a parameter of a function.

fun main(args: Array<String>) {
    listOf("1st parameter", "2nd parameter", *args)
}
val list = setOf(1, 2, 3, 4, 5)

println(list.map { it * 2 }) // [2, 4, 6, 8, 10]
println(list.filter { it > 2 }) // [3, 4, 5]
println(list.groupBy { it % 2 }) // {1=[1, 3, 5], 0=[2, 4]}

val cities = listOf("Berlin", "Hamburg", "München", "Köln", "Frankfurt")

// First character of the 3 cities with the shortest names
val result = cities
    .sortedBy { it.length }
    .take(3)
    .map { it[0] }
    .fold("") { acc, char -> acc + char }

println(result) // KBH

Loops

fun main(args: Array<String>) {
    for (arg in args) {
        println(arg)
    }
}

val items = listOf("apple", "banana", "kiwifruit")
for (index in items.indices) {
    println("item at $index is ${items[index]}")
}

val items = listOf("apple", "banana", "kiwifruit")
var index = 0
while (index < items.size) {
    println("item at $index is ${items[index]}")
    index++
}

for (x in 1..5) {
    print(x)
}

for (x in 1..10 step 2) {
    print(x)
}

Classes

You can only derive from a class if it is open

open class MyFooClass  {
    open fun foo() { println("Foo!") }
}

class MyBarClass: MyFooCass {
    override fun foo() { println("Bar!") }
}

You can extend classes directly without creating an explicit derived class

package MyStringExtensions

fun String.lastChar(): Char = get(length - 1)

println("Kotlin".lastChar())

More complex example (this is the object)

fun <T> MutableList<T>.swap(index1: Int, index2: Int) {
    val tmp = this[index1]
    this[index1] = this[index2]
    this[index2] = tmp
}

Classes can have a primary constructor in the class definition that does not have code but you can use its parameters

class Customer(name: String) {
    val customerKey = name.toUpperCase()
}

Or secondary constructors

class Person {
    var father

    constructor(parent: Person) {
        father=parent
    }
}

Visibility

public(default): Visible everywhere
internalVisible in a module
protectedVisible in subclasses
privateVisible in a class

Data Class

Property only classes

data class Person(val name: String = "", val id: Int = 0)

Objects to list of variables

In Kotlin a class can have a function to export some of the object's values. You can use a so called "Destructuring Declaration" to assign multiples variables from one object (this has nothing to do with the concept of destructors for object deletion).

val (name, age) = person

This will do the following and the class need to have the componentX methods:

val name = person.component1()
val age = person.component2()

If you don't need one of the variables you can skip it like this

val (_, age) = person

This also works for functions. Define a data class (it will have the componentX methods automatically), have your function return the data class, done

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

fun myFunction(): Result {
    return Result("Jon", 42)
}

val (name, age) = myFunction()

You can also use it in Lamdas and it will extract its parameters. This would expect for example one object that can be extracted into 2 variables:

{ (name,age) -> ... }

The normal syntax for 2 parameters in Lambdas would be

{ name,age -> ... }

Operator overloading

For each operation there is predefined method name and you also need to add the key word operator before the method.

data class Foo(...) {
    operator fun inc(): Foo {
        ...
    }
}

List of operator names

a++a.inc()
a--a.dec()
a + ba.plus(b)
a - ba.minus(b)
a * ba.times(b)
a / ba.div(b)
a += ba.plusAssign(b)
a -= ba.minusAssign(b)
a *= ba.timesAssign(b)
a /= ba.divAssign(b)
a == ba?.equals(b) ?: (b === null)
a != b!(a?.equals(b) ?: (b === null))
a > ba.compareTo(b) > 0
a < ba.compareTo(b) < 0
a >= ba.compareTo(b) >= 0
a <= ba.compareTo(b) <= 0
a..ba.rangeTo(b)
a in bb.contains(a)
a !in b!b.contains(a)
a[i]a.get(i)
a[i, j]a.get(i, j)

Objects as methods

If your class has an operator method

operator fun invoke()

you can call your object like a method even with parameters.