Kotlin at a Glance — A Language a Day, Advent Calendar 2019 Day 4/24

About this ‘A Language a Day’ Advent Calendar 2019

Welcome to Day 4 of this year’s A Language a Day Advent Calendar. Today’s topic is introduction to the Kotlin programming language.

Facts about the language

Some facts about the Kotlin programming language:

  • It targets JVM (but the syntax is not of Java)
  • Can be compiled to JavaScript or LLVM
  • Directly suitable for Android development
  • Appeared in 2011
  • Designed by JetBrains
  • Website: kotlinlang.org

Installing and running Kotlin

There are multiple options of installing Kotlin on your machine, here is the simplest way to get it on a Mac:

 $ brew install kotlin

To compile a program, use the following command-line call:

$ kotlinc helloworld.kt -include-runtime -d helloworld.jar

To run the generated jar file, pass it to Java:

$ java -jar helloworld.jar 

Alternatively, use the online playground.

Hello, World!

Here is the minimal Hello, World! program:

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

As you can see, the entry point is the main function, and the semicolons are not needed at the end of the line (but you can use them).

Variables

There are two keywords to create a variable: var and val. The first of them creates a mutable, the second one — an immutable variable.

fun main() {
    val greeting = "Hello"
    var name = "John"
    println("$greeting, $name!") // Hello, John!

    // greeting = "Hi" // error: "val cannot be reassigned"
    name = "Karla"
    println("$greeting, $name!") // Hello, Karla!
}

In this example, you can also see how Kotlin is interpolating variables in a string. Similarly to Perl, the variable is prefixed with a dollar to be interpolated, but unlike Perl, you use a regular name with no prefix everywhere else.

The type of the variable can be deduced by the compiler, but you can also help it by specifying the type explicitly:

var name: String = "John"

Functions

The main function, which we met in the Hello, World! program, is an example of a minimalistic function that takes nothing and returns nothing. But that is, of course, not the general rule, and even the main function can look differently.

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

This program prints the arguments passed via command line:

$ java -jar out.jar alpha beta gamma
alpha
beta
gamma

The same idea is for announcing the return type, for example:

fun make_greeting(name: String): String {
    return "Hello, $name!"
}

An interesting feature of Kotlin is the way you can express short functions. The above example can be rewritten like this, using the = sign instead of curly braces:

fun make_greeting(name: String) = "Hello, $name!"

You can provide a function with the default value of a parameter:

fun make_greeting(name: String = "buddy") = "Hey, $name!"

A Factorial example

In the following example, the factorial function became quite compact due to the use of the when construct and because the whole body is a single when block, with no traditional function body block with a pair of curly braces.

fun factorial(n: Int): Int = when {
    n < 2 -> 1
    else -> n * factorial(n - 1)
}

fun main() {
    println(factorial(4)) // 24
    println(factorial(5)) // 120
    println(factorial(6)) // 720
}

The when block has two branches, and the result of each is calculated after the ->.

Lambdas

There are a few ways of defining a lambda function, one of them is shown below. Here, an anonymous function is saved in an immutable variable:

val greeter = {s: String -> "Hi, $s!"}

fun main() {
    println(greeter("John"))
}

Classes

In Kotlin, the object-oriented system is quite diverse. First, here is an example that can be more or less understood by anybody with an OOP experience in other languages.

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

    fun info() {
        println("My name is $name, I am $age years old.")
    }
}

fun main() {
    var p = Person("John", 42)
    p.info()
}

Note that the name and the age data members are declared with different keywords: val and var. A part in parentheses after the name of the class is actually a shortened form of a constructor. You can do the same more verbosely:

class Person constructor(name: String, age: Int) { ... }

You don’t need a constructor if you don’t need one. To set the initial values of data members (if you have them), use the init method.

class Person {    
    var name: String
    var age: Int

    init {
        name = ""
        age = 0
    }
    
    fun info() {
        println("My name is $name, my age is $age.")
    }
 }

fun main() {
    var p = Person()
    p.name = "Linda"
    p.age = 24
    p.info()
}

Objects

There is another keyword, object, which can work in place of class, but in this case you will create a single object (singleton). Take the following simple class and create two instances of it:

class X {
    init {
        println("Class init")
    }

    fun info() {
        println(this)
    }
}

fun main() {
    var x = X()
    x.info()

    var y = X()
    y.info()
}

This program creates two different objects:

Class init
X@4ca8195f
Class init
X@65e579dc

Now, change class to object (and remove parentheses when creating variables).

object X {
    init {
        println("Object init")
    }

    fun info() {
        println(this)
    }
}

fun main() {
    var x = X
    x.info()

    var y = X
    y.info()
}

Having this change, you will have a single object in your code: it is created only once, and both x and y refer to the same object:

Object init
X@4ca8195f
X@4ca8195f

Getters and setters

For data members to behave more like properties, you can define your own getters and setters. Here is an example of both a setter and a getter methods that do some additional work when the value of age is read or changed.

class Person(name: String, age: Int) {    
    val name = name
    var age = age
        set(new_age) {
            println("Happy birthday, $name!")
            field = new_age
        }
        get() {
            println("Somebody asked for $name's age")
            return field
        }
}

fun main() {
    var p = Person("John", 42)
    p.age++ // both getter and setter are triggered here
}

Inheritance

By default, classes are final. To derive a child class, make the class explicitly open.

open class Animal

class Dog: Animal()
class Cat: Animal()

(This is a correct Kotlin program, by the way. All three classes are empty.)

The same consideration counts for redefining methods. You have to both specify them as open in the base class, and as override in the derived class.

An Inheritance and Polymorphism example

Abstract classes and abstract methods have to be explicitly marked as abstract. An abstract class by definition is an open class.

abstract class Animal {
    abstract fun speak(): String
}

class Dog: Animal() {
    override fun speak() = "Dog"
}

class Cat: Animal() {
    override fun speak() = "Cat"
}

fun main() {
    var zoo = arrayOf(Dog(), Cat(), Dog(), Cat())
    for (animal in zoo) {
        println(animal.speak())
    }
}

In this example, the body of the speak methods in children classes returns a string, so you can gain a few characters by not typing { return ... }.

Coroutines

Instead of looking at working with threads, or using the async/await pair, let us touch another interesting feature of Kotlin, coroutines. We’ll implement the Sleep Sort algorithm using them.

Sleep Sort using coroutines

Here is the full code.

import kotlinx.coroutines.*

fun main() = runBlocking<Unit> {
    val data = arrayOf(10, 4, 2, 6, 2, 7, 1, 3)

    var jobs = emptyArray<Job>();
    for (n in data) {
        jobs += GlobalScope.launch {
            delay(n * 50L)
            println(n)
        }
    }
    
    jobs.forEach {
        it.join()
    }
} 

For each data element, a job is launched. Each job waits n * 50L milliseconds and prints the number. Note that as the delay function expects a long integer, you have to provide it yourself (multiplying by an L-suffixed number solves this problem and also makes a delay a bit longer, as we wanted for the algorithm).

All the jobs are collected to the jobs array; this array is declared to keep the objects of the Job type. After the jobs are submitted, we wait them to join the main thread (it is a default variable in the loop, thus we don’t need to introduce it explicitly).

Finally, as join can be only used within a coroutine, the main function is actually a runBlocking<Unit>.

This code requires you to install the kotlinx.coroutines library, but if you only want to play with it, run our example online.

Get more

I hope that you got an impression that Kotlin is a very rich language. I would advise you to continue exploring it. As a starting point, use the following resources:

Please get the source code of Kotlin examples you’ve seen in this article on GitHub.

Next: Day 5. Modern C++

Leave a Reply

Your email address will not be published. Required fields are marked *