Kotlin: Practical Reference for Java Developers

Tiago Albuquerque
Jun 18, 2020 · 11 min read

A straight reference to Kotlin Programming Language

This article is a reference guide to Kotlin programming language to be used as a quick reminder of its keys topics, especially for Java developers. Hope it helps others developers eager to learn Kotlin as well. Enjoy!

Kotlin is a concise, safe, interoperable and tool-friendly programming language developed by Jetbrains. Kotlin supports both Object Oriented Programming and Functional Programming paradigms, and it is interoperable with Java, which means that it's possible to call Java code from Kotlin.

“Hello Kotlin”

The entry point of a Kotlin application is the main function.

The classical “Hello World” example would be like this:

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

The main function may or may not have arguments (unlike main method in Java class):

fun main(args: Array<String>) {
// code...
}

Variables and Constants

Kotlin uses the keywords var and val to declare variables and constants(read-only values). If you initialize a variable or value in declaration, Kotlin compiler can infers its type. By default, variables in kotlin are not assigned to null, unless you mark then with “?” to make then nullable.

// syntax:
var <var_name>: <var_type> = <var_value>
var <var_name>: <var_value> // infers type
// Examples:
val unmodifiedMessage: String = "unmodified"
var message: String = "Can be modified"
var inferredVar = "Inferred type var"

Null types

Kotlin handle nullable types differently from Java, trying to avoid NullPointerExceptions at runtime. Kotlin tries to bring this kind of error to compile-time, avoiding undesirable troubles in production.

The ‘?’ after variable declaration indicates that it can be assigned to a null value, which forces us to think if it is really what we want.

var var1: String = "cannot be null"
var var2: String? = null // can store null or non-null values

When a type is defined nullable, Kotlin compiler does not allow us to access its members/attributes/methods before checking its value:

var test: String?
if (test != null)
println(test.length)
// As shortchut we can use the safe access expression:
var test: String?
println(test?.length) // will evaluate to null if value not present
// It's possible to use default values with safe access:
var test: String?
println(test?.length ?: 0) // will evaluate to 0 if value not present

Kotlin has what is called “smart cast” which means that after check a type for nullability, the programmer can handle that type as if it is not nullable.

Functions

Aside from function main, we can create out own functions.

// Syntax:
fun <function_name>(<func_params>):<fun_return_type> {
// code...
}
// Examples:
fun greeting() { // no parameters
return "Hello"
}
fun
sumNumbers(a: Int, b: Int): Int {
return a + b
}

If the function returns just one expression, it is possible to convert it to expression body (inline) and omits the ‘return’ keyword, like these examples:

fun sumNumbers(a: Int, b: Int) = a + b 
fun
greeting(name:String) = println("Hello $name")
fun greeting() = "Hello" // inference of types

If the function has no return value, it implicit return ‘Unit’, which is like a void (no-meaningful value).

Functions parameters are defined as “values” (constants), favoring immutability, so it’s not possible to assign another value to the parameter received (and indeed it should not make sense).

fun greeting(name: String) {
name = "ChangedName" // does not compile!
val msg = "Hello $name"
println(msg)
}

By using the ‘default parameters’, if it is not informed value to a given parameter of the function, the default value is assigned:

fun greeting(msg: String = "Hi", name: String = "Unknown") { 
println("$message $name")
}
greeting() // will print "Hi Unknown"

It’s also possible to use ‘naming parameters’ in a function call, so it is not needed to set the proper order of arguments:

fun greeting(msg: String, name: String) {
println("$message $name")
}
// We can call the function with any params order
// if we inform its name:
greeting(name = "Tiago", msg = "Hello")

Conditional statements

Kotlin has the ‘if’ and ‘when’ conditional statements for use.

The first thing to notice is that both statements are expressions, which means that they return a value.

val max = if (a > b) a else b

The ‘if’ statement is very like Java syntax:

var greeting: String? = "Hello" 
if (greeting != null) {
println(greeting)
} else {
println("Hi")
}

The ‘when’ statement is more like the ‘switch .. case’ in Java, but no ‘break’ instruction is needed:

var greeting: String? = "Hello"
when(greeting) {
"Hello" -> println("$greeting World")
"Ola" -> println("$greeting Mundo")
null -> println("No World")
else -> println("Hi")
}
// "Hello World" will be printed

The ‘when’ expression can evaluate more than one value to a single response:

fun verifyAnswer(color: String) = when(color) {
"blue", "white" -> "cold"
"red", "yellow" -> "hot"
else -> "unknown"
}

We can use ‘when’ to evaluate boolean expressions to a pair of values, in which case there is no need to provide arguments to when clause:

// Evaluates boolean expression to a pair of values
val
score: Int = 10
val (result, color) = when {
score >= 5 -> "passed" to "blue"
else -> "fail" to "red"
}

When’ is also very useful to check types before perform actions (‘smart cast’ feature). Notice the ‘is’ keyword which is analog to ‘instanceof’ in Java:

// Given 'Cat' and 'Dog' are 'Pets' (inheritance)
when(pet) {
is Dog -> pet.bark()
is Cat -> pet.meow()
}

In Kotlin there isn’t a ternary operator like Java, but there is a simple syntax for nullable cases to set default values:

var name?:String
val nameToPrint = name ?: "no name" // assign value in case of null

Loops and Ranges

Kotlin has the same loop instructions as Java: ‘for’ and ‘while’.

The ‘while’ syntax is very like Java syntax:

while (<condition>) {
// code...
}
do {
// code...
} while (<condition>)

The ‘for’ syntax is a little bit different from java, using the keyword ‘in’:

for (<element> in <collection> {
// code...
}
// Example:
val languages = listoOf("Kotlin","Java","Javascript")
for (lang in languages) {
println(lang)
}

In Kotlin we can use ‘for’ to iterate over map structures too:

val map = mapOf(0 to "Kotlin", 1 to "Java")
for ((key,value) in map) {
println("$key : $value")
}

It's possible to access the element index while iterating using ‘for’ statement, using the collection method ‘withIndex()’:

val languages = listOf("Kotlin","Java","Javascript") 
for ((index,element) in languages.withIndex()) {
println("$index : $element")
}
// prints:
0 : kotlin
1 : Java
2 : Javascript

It's even possible to iterate over characters of a string:

for (ch in "hello") {
print(ch)
}

Kotlin has an interesting feature of iterating over ranges:

// iterate over closed numbers range:
for
(i in 1..5) {
print(i) // prints 12345
}
// using 'until', excludes the upper bound:
for (i in 1 until 5) {
print(i) // prints 1234
}
// using 'downTo', includes the upper bound:
for (i in 5 downTo 1) {
print(i) // prints 54321
}
// using 'step':
for (i in 2 until 10 step 2) {
print(i) // prints 2468
}

The ‘in’ function can also be used to check if an element belongs to a range or to a collection (the same as ‘contains()’):

val c = 'a'
print(c in 'a'..'z') // prints true
val list = listOf("Kotlin","Java")
print("Kotlin" in list) // prints true

Ranges can be of any Comparable type, including users defined ones, and can be assigned to a variable/value as well:

// Examples:
val intRange: IntRange = 1..5
val charRange: CharRange = 'a'..'z'
val dateRange: ClosedRange<Date> = startDate..endDate

And, of course, we can user the ‘forEach’ method in collection to iterate through it:

<collection>.forEach { }
<collection>.forEachIndexed { index, value -> <statements> }

Exceptions

Unlike Java, Kotlin does not have difference between checked and unchecked exceptions, and a function does not have to specify the exceptions it may throw, although exists the optional “@Throws” annotation in Kotlin.

It's used the keyword ‘throw’ to define an exception, and it can be assigned to a variable/value.

val result = 
if (param in 1..10)
param
else
throw IllegalArgumentException("The value is not allowed")

It is also possible to use the ‘try … catch’ structure to catch exceptions, noticing that ‘try/catch’ is an expression, meaning that it can return a value.

val result = 
try {
// code...
} catch {
// code...
}

Extensions Functions

Kotlin allow us to extend functions of a given class.

// Example using 'String':
fun String.lastChar() = this.get(this.length-1)
// and now we can use the new 'lastChar' method:
val c = "test".lastChar() // returns 't'
// Example using 'List':
fun List<Int>.sum(): Int {
var result = 0
for (i in this)
result += i
return result
}
// and now we can use the new 'sum' method
val sum = listOf(1, 2, 3).sum()

We can also have extension properties:

val String.lastIndex: Int 
get() = this.length -1
// Usage:
"test".lastIndex // results 3

Collections

Kotlin favors the creation of immutable collections, but any collection type (array, list, map, set, etc..), can be build with the suffix ‘…of’:

val langArr = arrayOf("Kotlin","Java")         // immutable array
val langList = listOf("Kotlin","Java") // immutable list
val langMList = mutableListOf("Kotlin","Java") // mutable list
val langMap = mapOf(1 to "Kotlin",2 to "Java") // map

Functional Programming Support

Kotlin allows the use of lambda functions, as we can do in Java as well since version 8. The basic syntax is the same in both languages:

// syntax: { <args> -> <body> }
// Example:
{ a: Int, b: Int -> a + b }

We can use lambdas as an argument to a function (high order function), and if the lambda is the last argument (or the only one), we can even move it out of the parenthesis to better readability, and even delete the parenthesis if they get empty.

If the argument type can be inferred, it can be omitted too, and if it is the only argument, it can be replaced for the keyword ‘it’.

// Different syntax, same result
list.any({ i: Int -> i > 5 })
list.any() { i: Int -> i > 5 }
list.any { i: Int -> i > 5 }
list.any { i -> i > 5 }
list.any { it > 5 } // look how simple and expressive this is! :-)

Operations in Collections

Like the ‘stream’ operations in Java (filter, map, reduce, count, find, any, all, none, flatMap, groupBy, max, distinct, …), we can do the same operations on collections in Kotlin, which is better understandable with some examples:

// given
val
names = listOf("Ozzy", "Max", "Tom")
val ages = listOf(99, 50, 40)
// operations
names.forEach(println(it)) // print elements
ages.filter { it < 60 } // returns [50, 40]
ages.partition { it < 60 } // return [50,40] and [99]
names.map { it.length } // returns [4, 3, 3]
names.last() // returns "Tom"

OOP and Classes

Kotlin uses implicit getters and setters for properties, so there is no need to declare these methods, unless we want a customized behavior to it. We can access or modify its value without prefix get or set too, which are invoked under the hood.

class Test {
var prop = 0;
}

We can define a class and pass the values for attributes/properties in its constructor as well (which becomes its primary constructor). If we use the val/var keyword, it creates automatically the class property, otherwise its just constructor parameters (no properties created, and you have to implement the initialization logic on ‘init’ block)

Notice that if we pass values as ‘var’, it is created implicit getters and setters, while if it is used ‘val’, only implicit getters are created.

// class definitions examples:
class Person
class Person(val firstName: String, val lastName: String) class Person(val firstName: String, val lastName: String) {
constructor(): this("Ozzy","Osbourne") {
println("secondary constructor")
}
}
class Person(name: String) { // no 'val/var', just plain param
val name: String
init {
this.name = name
}
}
class Person(val firstName: String = "Ozzy",
val lastName: String = "Osbourne") { }
class Person(val firstName: String = "Ozzy",
val lastName: String = "Osbourne") {
fun
printGreeting() { // method
println("Hello $firstName")
}
}
// class instantiations
val person = Person() // no 'new' keyword!
val person = Person("Darth","Vader")
// accessing attributes and methods
person.firstName
person.printGreeting()

Of course we can use customized getters and setters like shown below:

enum class State { ON, OFF }class StateClass {   private var boolVal = true   var state: State
get() = if (boolVal) ON else OFF
set(value: State) {
boolVal = value == ON
}
}

The default accessors in Kotlin are different from Java: any declaration is public and final by default. If you want something to be not final, you have to explicitly mark it as ‘open’.

Another difference from Java is that in Kotlin we can have several classes (and functions) in the same file, without the restriction of public class equals file name like in Java.

To represent inheritance, Kotlin uses different syntax from Java:

open class Parent
class Child : Parent()
open class Parent(val foo: String)
class Child(foo: String) : Parent(foo)

Kotlin has the useful concept of ‘data class’, when it is automatically generated some common methods like equals(), hashcode(), toString(), copy(), etc..

data class Test(val foo: String)

Kotlin compares objects for equality different from Java, which can be trick:

val set1 = setOf{1,2,3}
val set2 = setOf{1,2,3}
set1 == set2 // return true, calls equals() to compare content
set1 === set2
// return false, checks object reference

Using the ‘sealed’ keyword, we can restrict the class hierarchy, that means that all sub-classes must be located in the same file.

It's used the keyword ‘object’ to create a singleton instance on Kotlin. This keyword creates a class and an instance in the same time:

object Test {
fun foo() {
// code...
}
}

The ‘companion object’ is like a nested object inside a class, that means to replace the need for static methods in class like it's done in Java.

class T {
companion object {
fun foo() { println("foo") }
}
}
A.foo() // invokation

In Kotlin we can make use of generics just like in Java.

fun <T> List<T>.filter(predicate: (T) -> Boolean): List<T>

Interfaces

Classes can implement interfaces that defines contracts of methods and behavior:

interface PrinterInterface {
fun
printMsg(msg: String)
}
class SuperPrinter : PrinterInterface {
override fun
printMsg(msg: String) {
// method implementation
}
}

In Kotlin, interfaces can have default methods implementations and attributes (with no value assigned).

interface PrinterInterface {
val
info : String // attribute
fun
printMsg(msg: String) // method
}
class SuperPrinter : PrinterInterface {
override val
info : String // override attribute
get() = "Default Info"
override fun printMsg(msg: String) {
// method implementation
}
}

To implement multiple interfaces, we list then in declaration and implement all the abstracts methods of all the interfaces:

interface PrinterInterface {
val
info : String // attribute
fun
printMsg(msg: String) // method
}
interface IdentificableInterface {
fun getId()
}
class SuperPrinter : PrinterInterface, IdentificableInterface {
override val info : String // override attribute
get() = "Default Info"
override fun printMsg(msg: String) {
// method implementation
}
override fun
getId() {
// method implementation
}
}

Enums

Enums in Kotlin are similar to Java Enum, and they are declared and used as the example below:

// declarations
enum class GameDifficultyLevel {
EASY, MEDIUM, HARD
}
enum class
Operator(val op: Char) {
PLUS('+'), MINUS('-')
}
// use example
fun identifyLevel(level: GameDifficultyLevel) : String {
return when(level) {
GameDifficultyLevel.EASY -> "Selected Easy"
GameDifficultyLevel.MEDIUM -> "Selected Medium"
GameDifficultyLevel.HARD -> "Selected Hard"
}
}

I guess this covers the main topics of Kotlin syntax. Hope it be helpful!

Sign up for Top 10 Stories

By The Startup

Get smarter at building your thing. Subscribe to receive The Startup's top 10 most read stories — delivered straight into your inbox, once a week. Take a look.

By signing up, you will create a Medium account if you don’t already have one. Review our Privacy Policy for more information about our privacy practices.

Check your inbox
Medium sent you an email at to complete your subscription.

Tiago Albuquerque

Written by

Software Craftsman

The Startup

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +787K followers.

Tiago Albuquerque

Written by

Software Craftsman

The Startup

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +787K followers.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store