Kotlin: a new JVM language you should try
Rafal GancarzKotlin 1.0 is here
JetBrains (the people behind IntelliJ IDEA) have recently announced the first RC for version 1.0 of Kotlin, a new programming language for the JVM. I say ‘new’, but Kotlin has been in the making for a few years now, and has been used by JetBrains to develop several of their products, including Intellij IDEA. The company open-sourced Kotlin in 2011, and have worked with the community since then to make the language what it is today.
What is Kotlin and why you should care
There is no shortage of JVM languages to choose from these days. Kotlin (named after Kotlin island, near St. Petersburg, pictured above) positions itself as a pragmatic and easy-to-use alternative to Java, with which it is highly interoperable, to the point where you can mix Kotlin and Java code in the single codebase. The emphasis on interoperability lowers the adoption barrier, which can make Kotlin rather appealing to any Java developer who wished Java was more expressive and less verbose. Kotlin compiles to Java 6-compatible bytecode so you can use the powerful features it offers on older JVM versions (or Android where it has become quite popular already).
Hello World! with Kotlin
Let’s see some code and compare it with Java and Scala (from which Kotlin has borrowed quite a few language constructs) to get a first impression about the language and highlight a few differences compared to the alternatives.
// Kotlin
fun main(args: Array<String>) {
println("Hello, World!")
}
// Scala
object App {
def main(args: Array[String]) {
println("Hello, world!")
}
}
// Java
public class App {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
You can observe almost immediately how compact Kotlin’s version is (even compared to Scala). Kotlin benefits from top-level (or package-level) functions that are not members of a class or object, which is perfect for the main
function. Since package declaration is omitted the function belongs to the default package.
There are a few language features shown above that make Kotlin programs somewhat similar to Scala ones:
- Type declarations are defined after function argument names, variable names or function/method names
- the
Unit
return type (equivalent to Java void) is optional Array
is a regular generic type- The
println()
function, and other print functions, are available implicitly - semicolons are optional :)
Type system
Kotlin’s type system is close enough to Java’s to provide a good level of interoperability, although it still empowers developers to produce elegant code in a productive way; for instance, type inference removes the need to declare variable types everywhere. A lot of features in Kotlin have been influenced by the Effective Java series, and try to tackle some of the not-so-great elements that Java has inherited from its early days. Kotlin makes it easier to follow best practice, and prevents some of the more questionable practices completely.
The key features of Kotlin’s type system (compared to Java) are:
- every class inherits from type
Any
, which defines only 3 functions (equals()
,hashCode()
andtoString()
), compared to quite a few more in Java’sObject
- all data types are represented by classes, including numbers (
Int
,Long
,Double
, etc.), characters (Char
) and booleans (Boolean
) - a class’s primary constructor can be defined as part of the class declaration
class Foo(val bar: String, val baz: Int)
- the
new
keyword is missing
val foo = Foo("bar", 42)
- there are no static methods and any static code needs to be defined in a class companion object
- classes and functions are final by default and have to be declared
open
to be overridable - any type can be enhanced by using extension functions to define additional functions/methods (no more
xUtils
classes) - generics have been revamped to make working with variance much easier
- objects can be used to implement the singleton pattern or to avoid the need for anonymous inner classes
- data classes make it trivial to create Java Bean-style data containers
data class Person(val firstName: String, val lastName: String)
Functional programming
Kotlin is not a functional language but provides decent support for FP elements where they make sense in the OO world. It lifts functions to first-class-citizen status and makes using them very easy (the same sadly can’t be said about Java, even in version 8). Kotlin supports higher-order functions which means that functions can be passed in as arguments to other functions or returned from functions.
// List.map() is an example of a higher-order function
fun List.map(transform: (T) -> R): List {
val result = arrayListOf()
for (item in this)
result.add(transform(item))
return result
}
Additionally, lambda expressions and anonymous functions can be used when function expressions or references are expected.
val doubled = ints.map { it -> it * 2 }
Some other nice features around working with functions in Kotlin include:
- default argument values
fun read(b: Array, off: Int = 0, len: Int = b.size()) {...}
- named arguments
read(bytes, len = 100)
- infix notation
val plusOne = value + 1 // plus operator is a function call using infix notation
- Unit return is optional
fun printMe(message: String): Unit { println(message) }
- single-expression functions
fun printMe(message: String): Unit = println(message)
- tail-recursive functions
tailrec fun findFixPoint(x: Double = 1.0): Double = if (x == Math.cos(x)) x else findFixPoint(Math.cos(x))
Null safety
Handling null references has been quite a chore in Java from the very beginning. In recent versions of the language a few utility classes have been added to make it somewhat more manageable (Objects
in Java 7 and Optional in Java 8 come to mind), but the language itself doesn’t provide much-needed native support. Kotlin is different as it adopts and further enhances Groovy’s approach to null-safety and makes working with object references less of a hassle.
By default, the compiler will prevent assigning null value to an object reference and you have to explicitly declare a variable as nullable.
var a: String = "abc"
a = null // compilation error
var b: String? = "abc" // '?' declares variable as nullable
b = null // ok
With that initial safety feature in place you can then safely follow nullable object references without worrying about NullPointerException
(or employing an excessive degree of null-checking). Additionally, the so-called Elvis operator (surely you can see Elvis’ hair do in ?:
) provides a syntactic sugar for defining a default value if a null reference has been encountered.
val departmentHead = employee?.department?.head?.name ?: "n/a"
On top of that, Kotlin offers safe-casting support, where instead of throwing ClassCastException the expression returns a null value.
val aInt: Int? = a as? Int
Collections
Unlike Scala, Kotlin doesn’t provide its own collection framework built from ground up. The rationale behind that choice is Java interoperability. Collections are the bread and butter of software engineering and the team behind Kotlin decided they didn’t want to make it difficult to use Java collections from Kotlin, or to make developers to learn new collection types. Collections in Kotlin reflect those in Java with one difference – Kotlin distinguishes between immutable and mutable collections, favouring immutability (although mutable Java collections are being used behind the scenes).
val numbers: MutableList = mutableListOf(1, 2, 3)
val readOnlyView: List = numbers
println(numbers) // prints "[1, 2, 3]"
numbers.add(4)
println(readOnlyView) // prints "[1, 2, 3, 4]"
readOnlyView.clear() // -> does not compile
val strings = hashSetOf("a", "b", "c", "c")
assert(strings.size == 3)
Additionally, the collection API in Kotlin offers a range of easy-to-use extension functions around collection types.
val items = listOf(1, 2, 3, 4)
items.first() == 1
items.last() == 4
items.filter { it % 2 == 0 } // returns [2, 4]
items.filter { it % 2 == 0 } take 1 forEach { println(it) } // prints out 2
items.fold(0) { total, current -> total + current } // returns 10
Creating and using maps is quite convenient too.
val readWriteMap = hashMapOf("foo" to 1, "bar" to 2)
println(readWriteMap["foo"])
val readOnlyView: Map = HashMap(readWriteMap)
val doubled = readOnlyView.mapValues { entry -> entry.value * 2 }
for ((key, value) in doubled) println("$key -> $value")
Control flow
Kotlin offers some improved control flow constructs, certainly compared with Java. So for instance, the if ... else
block is an expression (returns a value).
val max = if (a > b) a else b
The switch
operator (as found in C and Java) has been replaced by when
expression form, similar to pattern-matching in Scala, that also makes a convenient replacement for if ... else
chains.
when {
x.isOdd() -> print("x is odd")
x.isEven() -> print("x is even")
else -> print("x is funny")
}
The when
expression supports smart-casts too, when combined with matching on the type of the when argument.
val hasPrefix = when(x) {
is String -> x.startsWith("prefix") // x is automatically casted to String
else -> false
}
A for
loop can iterate over any type that provides an iterator implementing next()
and hasNext()
.
for (item in collection)
print(item)
for ((index, value) in array.withIndex()) {
println("the element at $index is $value")
}
Other features
I think we’ve covered quite a lot already, but there are still a few more features worth highlighting to round up the language overview:
- string templates (known in Scala as string interpolation) make it easy to format string messages easily by resolving variables and expressions
val s = "abc"
val str = "$s.length is ${s.length}" // evaluates to "abc.length is 3"
- destructuring allows convenient extraction of data class or collection elements
val person = Person("Rafal", 22) // I wish I was 22 again!
val (name, age) = person
for ((key, value) in map) {
// Map.Entry can be destructured too
}
- type checks support smart-casting
if (x is String) {
print(x.length) // x is automatically cast to String
}
- structural equality can be checked simply with the
==
operator (no need to useequals()
) - referential equality can be checked with the
===
operator - creating ranges is supported natively
for (i in 1..10 step 2) println(i)
- predefined operators can be overloaded (so no crazy operators allowed but still quite powerful)
- all exceptions are unchecked (no more forced exception handling)
try
is an expression
val a: Int? = try { parseInt(input) } catch (e: NumberFormatException) { null }
- type-safe builders help creating rich DSLs
html {
head {
title {+"HTML built with Kotlin"}
}
// ...
}
By now I’m sure you are overloaded with all this Kotlin knowledge, so I will stop here. I hope I was able to showcase the entire host of very powerful capabilities that Kotlin offers. By all means explore the language reference to find out more about various language features.
Java interoperability
One of the design goals behind Kotlin has always been to stay close enough to Java to lower the barrier to adoption. Obviously, on the other hand the need to stay interoperable limits the the scope to introduce many new features into the language. I think Kotlin has managed to achieve quite a lot without breaking its ties to the Java world.
Most of the interoperability aspects revolve around mapping concepts or types between both languages.
For instance, since Java doesn’t offer null safety support similar to Kotlin, Java types are treated in a special way and called platform types. Null checks are relaxed for such types with the risk of NullPointerExceptions
being thrown at runtime.
val list = ArrayList() // non-null (constructor result)
list.add("Item")
val size = list.size() // non-null (primitive int)
val item = list[0] // platform type inferred (ordinary Java object)
item.substring(1) // allowed, may throw an exception if item == null
val nullable: String? = item // allowed, always works
val notNull: String = item // allowed, may fail at runtime
Similarly, Java generics are handled in a special way when types are imported to Kotlin and using Java arrays of primitives requires a workaround where specialised classes are used to represent them in Kotlin.
When it comes to Kotlin classes being called from Java, much of the work required to make them interoperable happens on the Kotlin side. Developers can use an range of annotations to instruct the compiler to produce code that will be usable from Java.
Tooling
One of the interesting aspects about Kotlin is that the language has been created by the company behind a popular IDE product. As one might expect IDE support (well, for Intellij IDEA at least) for Kotlin has been very much a part of working on the language itself. The result is a comprehensive Kotlin support in Intellij IDEA on a par with that for Java. JetBrains also maintains an Eclipse plugin for Kotlin so this open-source IDE should also provide a good level of support.
Kotlin creators have resisted the need to redefine much of the external tooling around the language. So for instance Kotlin projects can be built using Maven, Gradle or even Ant so except for the compiler plugin not much else is changing for those coming from the Java background.
Kotlin ecosystem
There is a growing community around the language so perhaps not surprisingly one can already find a good selection of open-source libraries and frameworks written in Kotlin. If you want to stick with your favourite Java framework, you don’t have to make a full transition to Kotlin as you can use your existing framework in Kotlin without too much hassle and even mix Java and Kotlin code in a single codebase.
The official Kotlin website lists several tools, libraries and frameworks, covering MVC/Web, HTTP client, dependency injection and text editor support among over things. Also, the Awesome Kotlin GitHub website offers a great selection of resources to explore.
Summary
I believe Kotlin deserves at least a try, especially if you are coming from the Java background. The language brings a lot of good features from Scala and Groovy closer to Java, so you can benefit from the best of both worlds: the vastness of the Java ecosystem with expressiveness and productivity enhanced by constructs and features coming from other languages. If you do want to give Kotlin a go, why don’t you try it online first or just create a Kotlin project in your favourite IDE and start experimenting with it.
Throughout this post I was covering Kotlin from the perspective of somebody coming from Java as I believe this is a primary expansion area for the new language. It’s worth knowing that Kotlin is also targeting Android development (also Java but still on version 6) as well as (still experimental) JavaScript runtime as an alternative to the JVM. With such a wide range of applications and some great features I reckon Kotlin stands a good chance of attracting quite a few new users.