Nov 7, 2019

Kotlin for developers - an introduction

Kotlin for developers - an introduction

What if we make a language that takes all the good parts from other popular languages?

The most closer answer is Kotlin.

Kotlin is gaining popularity now. Kotlin is 4th fastest growing language according to this report.

Let us explore it.


If you are coming from Javaland, Kotlin makes you write lesser code. It removes all the redundant things and makes your code elegant ✨ and concise ✅.

For example, you can write a simple hello world as below:

println("Hello World!!!")

If you are wondering that looks like scripting language, then you are most probably right. But the main difference is that Kotlin is statically typed and compiled language. When we execute the above println function, the function is compiled and executed.

It is a fun to write Kotlin functions:

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

No semicolons 🎉

No class definitions required 🎉💥

No default modifiers needed 🎉💥✨

The above function is a package-level function. When we decompile the code into Java it will be final static function inside the auto-generated class by Kotlin (based on the file name).

Klassy

Great, I love classes how will you define a class in Kotlin.

Kotlin makes it elegant and concise again:

class Greeting

That is it, it is a class definition, you don't even need a curly braces there to scope it. The compiler takes care of everything.

The Greeting class is a final class by default. If you are wondering why remember the Good old Effective Java definition.

Let us define functions inside the class.

class Greeting {
  fun sayHello(): String {
    return "Hello"
  }

  fun sayHelloWithName(name: String) = "Hello $name"
}

The sayHello is a classic function which returns a String Hello. Note that we define the return type using : String notation.

The second function sayHelloWithName takes an argument name that is String. Since it is a function with a single line we can use = sign and followed by the value that it returns. Again Kotlin tries to reduce the amount of boilerplate code.

Kotlin makes you enjoy the process of writing code by eliminating the boilerplate code. But do remember the compiler does that for you. This means compile time will be higher than compared with Java.


Constructors

In Object Oriented Programming, classes represent object. The objects will have member functions and constructors to initialize them.

For example, If you want to create an employee class with a constructor.

class Employee (name: String, salary: Long)

This is called as primary constructor.

We can initialise the Employee:

val employee = Employee("Raj", 100000L)

Note: there is no new word to initialize the object.

What if you want to add in an experience field for some employees who joins the company with some experience. We will use secondary constructors.

Kotlin provides a way to define multiple constructors using the constructor keyword.

class Employee (name: String, salary: Long) {
  constructor(name: String, salary: Long, experience: Int): this(name, salary)
}

We defined the secondary constructor with constructor keyword followed by the list of input arguments.

Note: When you define a secondary constructor along with a primary constructor then we should delegate the secondary constructor with the primary constructor.

We cannot define code in the primary constructor. This means if you want to define any code during the initialization you should either rely on constructor method or init method.

The init method is called when the class is initialised.

class Employee (name: String, salary: Long) {
  init {
    println("Employee ${this.name} is added")
  }
}

Initialiser method will be called before the secondary constructor is initialised.

You can have multiple init method inside a single class. The init methods are called in the order it is defined.


Inheritance

Kotlin provides an easy syntax for inheritance.

class Manager {}

Now Manager class has to extend the Employee class. At the end of the day they are employees right.

class Manager: Employee() {}

When you run the above code, the compiler will yell at you. The main reason is that the employee class has a primary constructor with two arguments. We need to pass in the arguments to the Employee like below

class Manager: Employee(name: String, salary: Long)

But wait from where we will get these inputs to the parent class? We will pass it through Manager's primary constructor.

class Manager(name: String, salary: Long): Employee(name: String, salary: Long)

Ofcourse, Manager should have their team right? We will pass them too.

class Manager(name: String, salary: Long, team: List<Employee>): Employee(name: String, salary: Long)

But the compiler still errors. Kotlin makes all the classes final by default.

If you want to inherit from a class, you should open it with an open keyword. That is as follows.

open class Employee(name: String, salary: Long)

class Manager(name: String, salary: Long, team: List<Employee>): Employee(name: String, salary: Long)

Now the compiler is happy. 😃

Kotlin also provides sealed classes read more about here.


data classes

Redundant and verbose Plain Old Java Objects take some rest. Kotlin's data class makes it concise and eliminates any boiler plate code. No getters and setters are needed.

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

The compiler will create everything automatically.


var and val

In Kotlin, immutable types are built in the library. The classes that you define are final, the methods are final, static too.

To define an immutable value we use the keyword val:

val message = "Hello"

Once defined val is immutable and you cannot change it.

However, being a friendly language, Kotlin also provides you with var.

The values defined with var are mutable.

var message = "Don't use me, it is a shame!!"

Null and !!, ?., ?:

Kotlin forces you to write null safety code. It tries to eliminate NPE at the compile time.

Kotlin compiles down to Java and then to JVM byte code. Java has Null Pointer Exception, so there can be instances in which you will get NPE.

var message = "Hello"
message = null

The above code will have a compilation error. But null is everywhere right.

In Kotlin, we can define an Optional type using String? that can accept null value. That is

var message: String? ="Hello"
message = null

The above code compiles. But when you use the above code elsewhere like

message.toUpperCase()

The compiler yells at you. That is Kotlin compiler will prevent you from invoking a possible null value at the compile time.

We can shut down the compiler by using any of the following syntax:

!! or ?. or ?:

The first (!!) is called not-null assertion operator or dirty operator. The double-bang tells the compiler, shut up and compile I know what I am doing.

message!!.toUpperCase()

This will not prevent runtime Null Pointer Exception.

The ?. is called Safe-call operator. This operator tells the compiler, call the function if the value is not null.

message?.toUpperCase()

The ?. is the sugar-syntax for

if (message != null) {
    message.toUpperCase()
}

The ?: is called elvis operator, it tells the compiler do this when not null else do the other thing.

message.toUpperCase() ?: "GIVE ME MESSAGE"

The ?: is the sugar-syntax for

if (message != null) {
    message.toUpperCase()
} else {
   "GIVE ME MESSAGE"
}

Functions

lambda

Kotlin also makes it easy to switch to functional context when needed. The functions are first-class citizen in Kotlin.

What does it mean first-class functions ? The functions can be used as an expression or data structure or passed around via arguments and return types.

val evenNumbers = 0..100.toList().filter { number -> number % 2 == 0 }

0..100.toList() creates a list of 0 to 100. Then we apply filter function (we don't need to stream them).

The filter function takes in a lambda function. We can use both () and {} syntax.

Then inside the lambda function we do the check for filtering even numbers and get the result. The final value returned is a list of numbers (and you don't need to collect them)

Well Kotlin makes it simple and concise right. The number above is redundant, we can eliminate that with it.

val evenNumbers = 0..100.toList().filter { it % 2 == 0 }

The it represents the current value from the list.

We can chain different functions together to make further transformations.

While Java is a more verbose, Kotlin makes it simple and concise.

Extension

One of the standout feature of Kotlin is extension functions. Suppose you have a type like Color which takes in a hex String and converts that into RGB value. You can create an extension function like below on some primitive types like String.

fun String.rgb() = this.removePrefix("#").windowed(2, 2).map { Integer.valueOf(it, 16) } // returns a list

println("#FFFFFF".rgb()) // 255, 255, 255

So what happens here,

We define a rgb extension function and attach it to the String base class. Now the extension function has this which represents the value on which we are calling the rgb method. Then we remove the prefix using removePrefix function. Once removed we window the string into 2 character chunks and on the result we convert the hexadecimal string into Integer. Finally return a list of numbers.

The code is elegant and also fits naturally as a function defined in String.

Note: we can also override the existing functionality with extension functions. So use it with care.

Inline

We can add inline keyword in a function definition. This will make the function inline at the call sites.

While inlining increases performance of the application, But beware that more you inline the functions the more the memory overhead will be.


Pattern matching a.k.a Type Checking

When I first learnt about pattern matching in Rust that was a mind blowing experience. I always wanted to have that feature in the JVM world. But at that time I couldn't find or know any.

With Kotlin we can have pattern matching to check the types. This reduces a ton of casting, boilerplate code. There is no need to cast an Object manually. The compiler does that for you more efficiently.

when (message) {
    is Int -> println("$message is a number")
    is String -> println("$message is a string")
    is Any -> println("${message} is any")
}

So what happens here. The is keyword is used for pattern matching. It matches the type of message.

When the message type is Int, the Int segment is called. When the message type is String, the String segment is called. When the message type is Any, the Any segment is called.

This feature is upcoming in Java - Check out my post on Java 13 here


Type casting

We can cast the type of an object using as keyword.

We can cast a type into another type like the following:

(1..10).toList().map { it as Any }

Any type is the base type and it is defined as the root of the Kotlin class hierarchy. Every Kotlin class has [Any] as a superclass.

So we are type casting the Integer into Any using as keyword.

Similarly we can also cast using as?, this will provide a safe-nullable type casting.


Check out this post on how to generate a Full Stack application with Kotlin, React and Spring Boot using KHipster here.

Wondering what is KHipster - check out here.


Up Next


யாதும் ஊரே யாவரும் கேளிர்! தீதும் நன்றும் பிறர்தர வாரா!!

@sendilkumarn