About this ‘A Language a Day’ Advent Calendar
This article became a part of my book ‘A Language a Day’, which you can get in both electronic and paper format. Languages covered in the book: C++, Clojure, Crystal, D, Dart, Elixir, Factor, Go, Hack, Hy, Io, Julia, Kotlin, Lua, Mercury, Nim, OCaml, Raku, Rust, Scala, and TypeScript.
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++
One thought on “Kotlin at a Glance — A Language a Day, Advent Calendar Day 4/24”