If you're an Android developer, you've probably heard of this cool new programing language on the block: Kotlin. Unless you've been living under a rock, you've also read about the recent announcement from Google during Google I/O that Kotlin is now the preferred programming language for Android.

The language Kotlin was named after the Kotlin Island, near St. Petersburg in Russia. It was chosen just as Java was the name of an island off Indonesia.

Kotlin is a cross-platform, statically typed, general-purpose programming language with support for type inference. It was designed by JetBrains (the company behind IntelliJ IDEA), and was designed to interoperate fully with Java. Kotlin mainly targets the JVM but also compiles to JavaScript or native code (via LLVM).

Just when you thought that you've mastered Java, here comes a new language! In this article, I'll walk you through Kotlin. I'm not going to assume that you have any specific background, but I'll try to discuss most of the language's features so that you can become productive straight away.

To try out Kotlin, use the latest version of Android Studio: Create a new Android application project and choose Kotlin as the language.

Declaring Constants and Variables

Kotlin supports two types of constants:

  • Runtime constants: The value of the constant can be assigned during runtime; once it's assigned, the value can't change anymore.
  • Compile-time constants: The value of the constant must be known during compilation time.

For runtime constants, use the val keyword. For example:

override fun onCreate(
savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState
    setContentView(R.layout.activity_main
    val URL = "http://calendar.learn2develop.net"
}

The val keyword allows you to assign a value to a constant during runtime. Once the value is assigned, the value can't be changed anymore. Using val is useful when the value of the constant is only known during runtime, such as from a user's input or the result returned from a function.

Declaring a reference using val makes it immutable and using var makes it mutable.

When declaring a variable or constant, you can directly assign it a value and the compiler infers the type automatically. In the previous example, the URL is automatically inferred to be of the String type. You can also declare the type explicitly, like this:

val URL:String = "http://calendar.learn2develop.net"

Alternatively, you can declare the constant with the type, and assign it a value later on:

val URL:String
URL = "http://calendar.learn2develop.net"

You need to explicitly declare a constant with its type if you are assigning it a value later on.

For compile-time constant, you can use the const keyword, like this:

package com.example.kotlinlanguage

import android.support.v7.app.AppCompatActivity
import android.os.Bundle

const val PI = 3.14

class MainActivity : AppCompatActivity() {    

    override fun onCreate(
    savedInstanceState: Bundle?)
    {        
        super.onCreate(savedInstanceState)        
        setContentView(R.layout.activity_main)    
    }
}

Do note that the const keyword can only be used at the top level, such as outside the class as in the last example.

Declaring Variables

To declare variables, use the var keyword:

var result = 0
ar str:String = "Hello, World!"

As with the val keyword, you can either let the compiler infer the type for you or you can declare its type explicitly. Note that if you declare a variable with a type but don't assign a value to it, trying to access it results in a compile-time error.

Type Alias

As with most modern programming languages, Kotlin also supports type alias. Type alias allows you to have alternative names for classes. It's useful for ensuring consistency and enhancing readability of your code. The following shows an example:

typealias CustomerIDType   = Int
typealias CustomerNameType = String
class MainActivity : AppCompatActivity() {    

    override fun onCreate(
    savedInstanceState: Bundle?)
    {  
        super.onCreate(savedInstanceState)        
        setContentView(R.layout.activity_main)        
        
        var customerID: CustomerIDType = 12345        
        var customerName: CustomerNameType = "Samatha Choo"    
    }

Tuples (Pair/Triple)

The latest version of Kotlin removes the support for the Tuple class. Instead, it has the Pair and Triple classes for supporting tuples of two and three members, respectively. For tuples of more than three members, you should instead be using Data Classes (more on this later in the Data Class section).

A tuple is a data structure consisting of multiple ordered parts (or members).

The following statement shows two tuples created using the Pair and Triple classes:

val pt:Pair<Int, Int> = Pair(7,8)
// using type inference
val flight = Triple(7031, "ATL", "ORD")

To extract each member in the tuple, you can assign it to a set of references (using var or val):

val (flightno, orig, dest) = flight
print(flightno)  //---7031---
print(orig)      //---ATL---
print(dest)      //---ORD---

Or more directly, access each member using the first, second, or third (only valid for Triple types) properties:

print(flight.first)  //---7031---
print(flight.second) //---ATL---
print(flight.third)  //---ORD---

Null Safety

Like most modern programming languages, Kotlin aims to eliminate the dangers of null references in your code.

Nulls

To make a variable a nullable type, prefix its type with the ? sign, like the following example:

var insurance_no:String?insurance_no = "12345678"

In the above example, insurance_no is a nullable string type, which means it can contain a string value or be set to null:

insurance_no = null

Safe Call Operator - ?

To use a nullable reference, it's important to check that it's non-null before accessing it:

if (insurance_no != null) {
    val length = insurance_no.length
}

Instead of explicitly checking the variable for non-null values, you can use the Safe Call Operator (?) like this:

insurance_no = null
val len = insurance_no?.length
// len is null

The ? operator evaluates the variable to see if it's null. If it is null, it returns null as the result. Otherwise, the length property is accessed and its value returned. Because of this, len is inferred to be of type Int?.

To perform an action if a variable isn't null, you can use the ?.let {} syntax, like this:

insurance_no = "12345678"
insurance_no?.let {
Log.d("", it.length.toString()) // 8}

The it keyword refers to the implicit name of the single parameter in a lambda function (the { } is known as a lambda function; more on this later). In this example, it refers to insurance_no.

Here is one more example:

val fruits:List<String?> =
    listOf("apple", "orange", null, "pineapple")
for (fruit in fruits) {
    fruit?.let {
        Log.d("", it)
    }
}

What if you want to print a statement whenever a null is encountered in the list? You can use the run function:

for (fruit in fruits) {
    fruit?.let {        
        Log.d("", it)    
    }?:run {        
        Log.d("", "Item is null")    
    }
}

Non-null Assertion Operator - !!

If you're sure that a variable is definitely not null, you can use the Non-null Assertion Operator (!!), like this:

insurance_no = "12345678"
val length = insurance_no!!.length
// length is implicitly declared as Int

In the event that insurance_no is indeed null, an exception will occur.

Elvis Operator - ?:

Very often, I check to see if a reference is a null before I assign its value to another variable, and assign it a default value if it is null:

val genderOfCustomer =
    if (gender == null) "male" else gender
// genderOfCustomer is now male

Using the Elvis Operator (?:), the above code could be rewritten as follows:

var genderOfCustomer = gender ?: "male"
// genderOfCustomer is now male

gender = "female"
genderOfCustomer = gender ?: "male"
// genderOfCustomer is now female

Here's another example:

var insurance_no:String?
insurance_no = null

val length = insurance_no?.length ?: -1
// length is -1

Type Casting

Kotlin doesn't differentiate between primitive type and reference types; All types are objects. Kotlin supports the following number types:

  • Long: 64 bits
  • Int: 32 bits
  • Short: 16 bits
  • Byte: 8 bits
  • Double: 64 bits
  • Float: 32 bits

When declaring types, the compiler can automatically infer the types for you:

val num1 = 5     // Int
val num2 = 1.6   // Double
val num3 = 3.4F  // Float

To convert values from one type to another, you need to explicitly call the respective conversion functions:

val num4 = num1.toFloat()  // 5.0
val num5 = num2.toInt()    // 1

You can convert a number to a string using the toString() function:

var str = num3.toString()  // "3.4"

Alternatively, you can use string interpolation (more on this later):

str = "$num3"

To convert a string to float, you can use the toFloat() function:

val num6 = str.toInt()

Strings

In Kotlin, strings are represented using the String class, enclosed using a pair of double-quotes (""):

var str1 = "A string"
var str2:String = "A string"

You can concatenate strings using the “+” operator, like this:

var str3 = str1 + str2
var str4 = "A" + " " + "String"

Or you can use string interpolation by prefixing the string reference with the “$” character:

var firstName = "Wei-Meng"
var lastName:String = "Lee"

var fullName = "$firstName $lastName"
// "Wei-Meng Lee"

If you want to use the “$” in an interpolated string, use the "" character to turn off the special meaning of "$":

var s = "Value of \$firstName is $firstName"
// "Value of $firstName is Wei-Meng"

Character and Unicode

To represent a single character, use a pair of single-quotes (‘'):

var c1 = 'C'
var c2 :Char = 'D'

Kotlin also supports Unicode characters using the Unicode Escape sequence syntax (\uxxxx):

val hand = '\u270B'
// hand icon
val star = '\u2b50'
// star icon

Functions

Functions are the fundamental building blocks in any programming language. Kotlin supports functions with:

  • Default Parameter value
  • Variable arguments
  • Named arguments

Defining Functions

To define a function, use the fun keyword, followed by the function name, parameters (if any), and the return value type (if any). The following code snippet shows a function named addNums with three parameters and a return value of type Int:

fun addNums(num1:Int,            
            num2:Int,            
            num3:Int):Int {    
    return num1+num2+num3
            }

Calling the function is straight-forward; pass in the required arguments and assign the return value to a reference:

val sum = addNums(5,6,7)

Here's another function with no parameters and no return value:

fun doSomething() {    
    // statement(s)
}

Here's another function that returns a Pair as its result:

fun countNumbers(string:String): Pair<Int,Int>{
    var odd = 3    
    var even = 4    
    // do something    
    return Pair(odd, even)
}
val count = countNumbers("12345")

Named Argument

Although arguments are passed to functions based on their position (known as position-based arguments), using named arguments makes your code more readable:

val sum = addNums(
    num1 = 5, num2 = 6, num3 = 7)

Using a named argument, arguments are passed in based on the names of the parameters. You can rearrange the position of the parameters using named arguments:

val sum = addNums(
    num2 = 6, num1 = 5, num3 = 7)

You can also mix position-based arguments with named arguments, as long as position-based arguments are placed before named arguments, like this:

val sum = addNums(5, num2 = 6, num3 = 7)

The following statement raises a compiler error because the third argument is unnamed:

// need to name the third argument
val sum = addNums(5, num2 = 5, 7)

Default Parameter Value

You can specify an optional default parameter in a function, like this:

fun join(firstName:String,         
         lastName:String,         
         joiner:String=" "):String {    
    return "$firstName$joiner$lastName"
}

In the above example, the joiner parameter is optional, with a default value of a space. This means that when you call this function, you can either pass in two arguments or three:

// with three arguments
var fullName =    
    join("Wei-Meng", "Lee", ",")
// Wei-Meng,Lee

// with two arguments      
fullName = join("Wei-Meng", "Lee")
// Wei-Meng Lee

Variable Arguments

If you need a function to accept a variable number of arguments, you can use the vararg keyword to indicate that the caller can pass in variable number of arguments. The following example accepts variable number of arguments via the parameter named nums:

fun average(vararg nums:Int):Float {    
    var sum: Float = 0F    
    for (num in nums) {        
        sum += num.toFloat()    
    }    
    return sum/nums.size
}

To access all the values in the variable parameter, you iterate through the nums parameter. The following code snippet shows three calls to the function with different number of arguments:

var avg = average(1,2,3,4,5,6) // 3.5
avg = average(1,2,3) // 2.0
avg = average(1,2,3,4,5) // 3.0

Collections

Kotlin supports a few data structures to hold groups of data. They are:

  • Arrays
  • Lists
  • Maps

Arrays and Lists

An array is a mutable collection of items of the same type. The following example shows an array called names initialized with five empty strings:

val names = Array<String>(5) {""}
// 5 elements all containing ""
// array size is fixed at 5 elements

You can also declare an array and assign it an array of items using the arrayOf() function:

val OSes: Array<String> =  arrayOf("iOS", "Android", "Windows Phone")
// array size is fixed at 3 elements

You can access the items in the array using its index:

val os = OSes[0] // iOS

And arrays are mutable:

// Array is mutable
OSes[2] = "Fuchsia"

Besides array, you can also use lists in Kotlin:

var items: List<Int> = listOf(1,2,3,4,5)

The above snippet declares a list of integer types containing five values. You can retrieve their values through their indices:

var item1 = items[0] // 1
var item2 = items[1] // 2
var item3 = items[2] // 3

However, unlike array, lists are immutable, so the following is not allowed:

// error
items[3] = 9 // list is immutable

Both array and list suffer from the same limitation: The size of an array/list is fixed after initialization. If you need to change the size of the collection at runtime, you can't use array or list. Instead, you need to use a MutableList:

var wishes: MutableList<String> = mutableListOf()

The above example declares wishes as a MutableList. During runtime, you can add or remove items from the mutable list:

wishes.add("iPhone")
wishes.add("iPad")
wishes.add("Samsung Fold")
wishes.removeAt(1)
wishes.remove("Samsung Fold")

Maps

Maps (more commonly known as dictionaries) contain key/value pairs of data. In Kotlin, you create a map using the hashMapOf() function, like this:

val platforms1 = hashMapOf(    
    "Apple" to "iOS",    
    "Google" to "Android",    
    "Microsoft" to "Windows Phone"
)

The above uses compiler inference to deduce the type of the key and value. If you want to declare it explicitly, you can do it the following way:

val platforms1:HashMap<String, String>

To obtain the value of a key, use the following syntax:

val p1 = platforms1["Apple"]   //---"iOS"---
val p2 = platforms1["Samsung"] //---null---

You can get detailed information about the map, such as size, keys, and values:

val count = platforms1.size.     // 3

val companies = platforms1.keys
// MutableSet of "Apple", "Google", and "Microsoft"
val oses = platforms1.values
// MutableCollection of "iOS", "Android", and "Windows Phone"

Note that because all the keys in a map must be unique, the keys property returns a MutableSet of values (String in this example). And because the values in a map need not be unique, the values property returns a MutableCollection of values (also String in this example).

You can add additional key/value pairs to the map:

platforms1["Samsung"] = "Bada"

Here is another example of a map:

val months:HashMap<Int, String> = hashMapOf()
months[1] = "Jan"
months[2] = "Feb"

Control Flow

Kotlin requires the use of the parentheses when evaluating an expression in an If statement:

val num1 = 5
val num2 = 6
var max:Int
if (num1>num2) {    
    max = num1
} else {    
    max = num2
}

If an expression evaluates to true, the block of statement to be executed is enclosed in a pair of braces ({}). If there's only one single statement to be executed, the pair of braces is then optional.

Because an if statement is an expression in Kotlin (i.e., it returns a value), there's no ternary operator in Kotlin. Instead, you can simplify the above statements as follows:

max = if (num1>num2) num1 else num2

The When Statement (Switch)

Instead of using the common switch statement, Kotlin replaces it with the more powerful and concise when statement. The following shows an example:

var grade: Char = 'B'
when (grade) {    
    'A', 'B', 'C', 'D' -> { print("Passed") }    
    'F' -> { print("Failed") }    
    else -> { print("Undefined") }
}

Observe that you can perform multiple checks separated with a comma (,), and there's no need to specify the break keyword. The when statement can also be used to return a value, as the following example shows:

var grade: Char = 'B'
var result = when (grade) {    
    'A', 'B', 'C', 'D' -> { 
    "Passed"    
    }    
    'F' -> {        
        "Failed"    
    }    
    else -> {        
        "Undefined"    
    }
}

Remember that the when expression must be exhaustive, and the else branch ensures that.

The when statement can also be used to match ranges, as the following example shows:

var percentage: Int = 85
when (percentage) {    
    in 0..20 ->        
        print("Group 1")    
    in 21..40 ->        
        print("Group 2")    
    in 41..60 ->        
        print("Group 3")    
    in 61..80 ->        
        print("Group 4")    
    in 81..100 ->        
        print("Group 5")    
    else ->        
        print("Invalid percentage")}

Looping Constructs

Kotlin supports the usual loop constructs available in other languages. They are:

  • while
  • for
  • repeat

However, the old C-style for loop is no longer supported. Instead, the for loop iterates anything that provides an iterator, like the following examples:

// prints 0 to 4            
for (i in 0..4) {    
    print(i)
}

// prints each item in the array
for (item in arrayOf(
    "Swift","Kotlin","Python")) {    
    println(item)
    }

The while loop continues execution as long as the condition is true:

// prints 0 to 4            
var count = 0
while (count < 5) {    
    print (count)    
    count += 1
}

The repeat loop executes a block of statements specified number of times:

// prints 0 to 4      
count = 0
repeat (5) {    
    print(count)    
    count += 1
}

Range Operators

Kotlin supports the “..” range operator to generate a range of values. First, the following example demonstrates how to implement a closed range operator using the “..” operator:

//---prints 5 to 9 inclusive---
for (num in 5..9 step 1) { print(num) }

To generate the numbers in descending order, use the downTo keyword:

//---prints 9 to 5 inclusive---            
for (num in 9 downTo 5 step 1) { print(num) }

To implement a half-open range operator, use the until keyword:

//---prints 5 to 8---            
for (num in 5 until 9 step 1) { print(num)}

Object-Oriented Programming

You declare a class in Kotlin using the class keyword:

class MyLocation {}

You can create an instance of the class using the class's default constructor:

// using type inference
val loc1 = MyLocation()

// declare and initializ
eval loc2:MyLocation = MyLocation()

Observe that there is no need for the new keyword when instantiating an object, as is common in other languages.

Properties

Creating properties in classes in Kotlin is straight forward: declare them using var or val:

class MyLocation {    
    var lat:Double? = null    
    var lng:Double? = null    
    val arrived:Boolean = false
}

In the above example, lat and lng are read/write properties and arrived is a read-only variable. Implementing getters are setters are optional. Take note that all properties must be initialized when you declare them (with the exceptions discussed in the next two sections).

The properties of the MyLocation class can be accessed like this:

val loc = MyLocation()
loc.lat = 57.474392
loc.lng = 37.228008
print(loc.arrived)

Also note that in Kotlin, there are four visibility modifiers:

  • public (default): Any client who sees the declaring class sees its public members
  • private: Visible inside the class only (including all its members)
  • protected: Same as private, plus visible in subclasses too
  • internal: Any client inside the module who sees the declaring class sees its internal members

When assigning values to properties, you can use the with function to provide scoping:

with(loc) {    
    lat = 57.474392    
    lng = 37.228008
}

Late Initialization for Properties

When you declare a property with a type, it must be initialized at the time of declaration unless it's a null type. For example, the following is invalid:

class MyLocation {
...      
}

class MainActivity : AppCompatActivity() {    
    var loc3:MyLocation     // error    
    
    override fun onCreate(
    savedInstanceState: Bundle?)
    {        
        super.onCreate(savedInstanceState)        
        setContentView(R.layout.activity_main)    
    }
}

In this case, loc3 is a property of the MainActivity class and it must be initialized when it's declared. To resolve this, either you either initialize it like you did before:

var loc3:MyLocation = MyLocation()

Or, use the lateinit keyword:

class MyLocation {
...      
}

class MainActivity : AppCompatActivity() {    
    lateinit var loc3:MyLocation  // late init

    override fun onCreate(
    savedInstanceState: Bundle?)
    {        
        super.onCreate(savedInstanceState)        
        setContentView(R.layout.activity_main)        
        loc3 = MyLocation()   // init later    
    }
}

The lateinit keyword specifies that the property will be initialized at a later time. Note that you have to remember to initialize the property before you first use it. If not, your application will crash when you try to use the uninitialized property.

Late initialization only applies to mutable types (var).

Lazy Initialization

Another concept that's similar to late initialization is called lazy initialization. Sometimes you have properties that have long and lengthy initialization processes. You want to make sure that you only perform these lengthy processes when you're certain that you're going to make use of these properties. In this case, you can use the by lazy delegate, like in the following example:

public class Example{    
    val name: String by lazy { "Johnson" }
}

In the above example, name is declared for lazy initialization. Let's try to create an instance of Example now:

val e = Example()

At this point, the name property hasn't been initialized yet. It's only when you reference it that it's initialized, like this:

val name = e.name  // "johnson"

Lazy initialization only applies to immutable types (val).

Delegated Properties

Kotlin supports a concept known as delegated properties. Using delegated properties, you can add listeners to properties so that you can be notified when changes are made to those properties. Consider the following example:

class MyPointClass {    
    var x: Int by Delegates.observable(0) {        
        prop, old:Int, new:Int ->        
        print(prop.name + " : from $old to $new")    
    }
}

After the declaration of the x property, I used the observable() method from the Delegates class to add a listener for changes to the property. The first argument of observable() is the initial value for the property, and the second argument is the handler to call when the property changes its value. Within the handler, you'll be able to get the value of the property before and after it was assigned.

When you now make changes to the properties, you see the following output:

val pt = MyPointClass()
pt.x = 5 // x : from 0 to 5
pt.x = 6 // x : from 5 to 6

Besides the observable() method in the Delegates class, there's another method called vetoable(). Like the observable() method, you can get the old and new value of the property. In addition, you're able to veto (hence its name) the modification of the property. Consider the following example:

class MyPointClass {    
    var x: Int by Delegates.vetoable(0) {        
        prop, old:Int, new:Int ->        
        print(prop.name + " : from $old to $new") new > 0    
    }
}

When you try to assign a 0 to the x property, it rolls back to its previous value:

val pt = MyPointClass()
pt.x = 5 // x : from 0 to 5
pt.x = 6 // x : from 5 to 6
pt.x = 0 // x will roll back to 6

Primary Constructor

Kotlin has a concise syntax for declaring properties and initializing them from what is called the primary constructor. Consider the following example:

class MyLocation (
    var lat:Double, var lng:Double) {    
    val arrived:Boolean = false
}

In the primary constructor, you declared two properties:

(var lat:Double, var lng:Double)

To initialize the two properties when you create an instance of the class, you can do this:

var loc = MyLocation(57.474392,37.228008)

You can then access the properties as usual:

print(loc.lat)
print(loc.lng)
print(loc.arrived)

Primary Constructor with Initializer Block

The primary constructor can't contain any code, only type parameters. In the previous section, you had the var keyword (you can also use val for read-only properties) prefixing lat and lng in the primary constructor:

(var lat:Double, var lng:Double)

With the var keyword, lat and lng become properties of the class. But if you remove the var keyword, the properties aren't created in the class. Consider the following example:

class MyLocation (lat:Double,                  
                  lng:Double) {    
    val arrived:Boolean = false    
    var latitude = lat    
    init { print("lat initialized as $lat") }    
    var longitude = lng    
    init { print("lng initialized as $lng") }
}

The latitude and longitud properties are now created in the class itself, and there are two init blocks within the class. These two init blocks are executed in the order they appear in the class. The init blocks are known as initializer blocks and allow you to write code when properties are initialized.

You can now use the MyLocation class as follows:

var loc = MyLocation(57.474392,37.228008)
print(loc.latitude)
print(loc.longitude)
print(loc.arrived)

Secondary Constructor

A secondary constructor has the constructor keyword followed by the parameters of the constructor. The following shows an example:

class MyLocation {    
    var lat:Double    
    var lng:Double    
    var arrived:Boolean = false    
    
    // secondary constructor    
    constructor(lat:Double,lng:Double) {        
        this.lat = lat        
        this.lng = lng    
    }    
    
    // secondary constructor; chaining    
    constructor(lat:Double,
                lng:Double,
                arrived:Boolean): this(lat,lng) {        
                this.arrived = arrived    
    }
}

In this example, there are two secondary constructors (and no primary constructor): the first with two parameters and the second with three parameters. The second secondary constructor calls the first secondary constructor:

constructor(lat:Double,lng:Double,
arrived:Boolean): this(lat,lng) {

If the class has a primary constructor, it's mandatory for the secondary constructor to call the primary constructor:

class MyLocation (var lat:Double,
                  var lng:Double) {    
    var arrived:Boolean = false
/*           
// This secondary constructor must be
removed as it conflicts with the primary
constructor          
constructor(lat:Double,lng: Double) {
    this.lat = lat
    this.lng = lng          
}          
*/

// secondary constructor; calls the
// primary constructor          
constructor(lat:Double,lng:Double,        
    arrived:Boolean): this(lat,lng) {        
    this.arrived = arrived    
    }
}

var loc = MyLocation(57.474392,37.228008)
var loc2 = MyLocation(57.474392,37.228008, true)

Custom Getter and Setter

So far, all of the properties are created by declaring the references using either var or val. In Kotlin, setters and getters are optional and are auto-generated if you don't create them in your class. However, you may want to create your own setters and getters, using the set and get keywords, respectively:

class MyLocation {    
    var lat:Double = 0.0    
    set(value) {        
        if (value > 90 ||            
            value < -90) {            
            throw IllegalArgumentException("Invalid latitude")        
        }        
        field = value    
    }    
    var lng:Double = 0.0    
    set(value) {        
        if (value > 180 ||            
            value < -180) {            
            throw IllegalArgumentException("Invalid longitude")
        }        
        field = value    
    }    
    val arrived:Boolean    
    get() {        
        return (this.lat in 57.0..58.0               
        && this.lng in 37.0..38.0)    
    }
}

In the example above, you created two setters: one for the lat property and the other one for the lng property. Using the setter, you can ensure that only correct values for the latitude and longitude can be assigned, or else an exception will be raised. For the arrived property, only a getter is defined (because it's a read-only property).

A good example to illustrate the usefulness of setters and getters is the following:

class Distance {    
    var miles = 0.0    
    var km: Double    
    set (km) {        
        miles = km / 1.60934    
    }    
    get() {        
        return 1.60934 * miles    
    }
}

In the Distance class, you have a property named km (for kilometers). When someone sets the value for the km property, you use the setter (set) to convert the value in kilometers to miles. And when someone tries to access the km property, you use the getter (get) to convert the value from miles to kilometers, like this:

var d = Distance()
d.km  = 10.0
print(d.miles) // 6.2137...

d.miles = 2.0
print(d.km)    // 3.21868

Enumerations

In Kotlin, enumerations are defined using the enum keyword. The following code snippet declares an enumeration called BagColor, with each member of the enumeration having an integer value:

enum class BagColor(val v:Int) {    
    Black(1),    
    White(2),    
    Red(3),    
    Green(4),    
    Yellow(5)
}

Here's another example:

enum class Membership(val color:String) {    
    Silver("silver"),    
    Gold ("gold"),    
    Platinum("platinum")
}

This time, each member of the Membership enumeration has a string value.

The value of each member of an enumeration is not limited to a single value; they can have multiple values, such as in the following example:

enum class DayOfWeek(
    val short:String, val long:String) {    
    Sunday ("Sun","SUNDAY"),    
    Monday ("Mon","MONDAY"),    
    Tuesday ("Tue","TUESDAY"),    
    Wednesday ("Wed","WEDNESDAY"),    
    Thursday ("Thu","THURSDAY"),    
    Friday ("Fri","FRIDAY"),    
    Saturday ("Sat","SATURDAY"),
}

Let's see how they're used. For the first example, you can declare a reference to be of type BagColor and assign it a color of Red:

var colorOfBag:BagColor
colorOfBag = BagColor.Red

You could convert the enumeration member to a string:

var colorStr:String = colorOfBag.toString()
// colorStr is now "Red"

Or obtain its member value through the v reference:

val color = colorOfBag.v
// color is now 3

The same goes for the Membership enumeration:

var member: Membership
member = Membership.Gold
var memberStr = member.color
// memberStr is now "gold"

For the DayOfWeek enumeration, you can access both the long and short values of the enumeration member:

var day:DayOfWeek
day = DayOfWeek.Friday
val s1 = day.long  // "FRIDAY"
val s2 = day.short // "Fri"

You can also convert a string value into an enumeration member using the valueOf() function:

day = DayOfWeek.valueOf("Saturday")
// day is now DayOfWeek.Saturday

Class Inheritance

By default, all classes in Kotlin are final. That is, they can't be inherited unless you specify otherwise. To make a class inheritable, use the open keyword, like this:

open class BaseClass(arg1:String) {    
// class has a primary constructor with 1 parameter
}

The BaseClass above has a primary constructor with one parameter. You can have another class, say SubClass1, that inherits from it:

class SubClass1(
    arg1:String,arg2:String):BaseClass(arg1) {
}

In this example, I'm initializing the BaseClass via the class header. I can also initialize the BaseClass via the secondary constructor using the super keyword, like this:

class SubClass2:BaseClass
{    
    constructor(arg1:String,                
                arg2:String):super(arg1) {
    }    

    // constructor chaining    
    constructor(arg1:String):this(arg1, "") {
    }
}

Here, SubClass2 has two secondary constructors: the second one chains to the first one and the first one calls the primary constructor of the base class.

Instance Methods

Instance methods are functions within a class:

class Car {    
    var speed = 0        

    fun accelerate() {        
        //...    
    }        

    fun decelerate() {
        //...    
    }        

    fun stop() {        
        //...        
    }        

    fun printSpeed() {        
        //...        
    }
}

You need to create an instance of the class before you can call the method:

val c = Car()
c.accelerate()
c.decelerate()
c.stop()
c.printSpeed()

Companion Object (Static Method/Property)

In the previous section, there are instance methods. Kotlin also supports static methods: methods from a class that can be called without needing to instantiate the class. In Kotlin, static methods are implemented through companion objects. Companion objects are objects that are common to all instances of a class. So, for the Car class, you can add the following companion objects: MilesToKM and kilometersToMiles():

class Car {    
    companion object {
        val MilesToKM = 1.60934        
        fun kilometersToMiles(km:Float):Double {
            return km / 1.60934        
        }    
    }    
    fun accelerate() {}    
    fun decelerate() {}    
    fun stop() {}    
    fun printSpeed() {}
}

Without instantiating the Car class, you can now access the MilesToKM property and kilometersToMiles() function:

val v = Car.MilesToKM
val miles = Car.kilometersToMiles(10F)

Overriding and Overloading Methods

When your class inherits from another class, it's common that you need to override or overload some of the methods available in the base class. Consider the following example:

open class Shape(length:Float,
                 width:Float) { 
    var length:Float    
    var width:Float

    init {        
        this.length = length
        this.width = width  
    }

    open fun perimeter():Float {        
        return 2 * (length + width)    
    }

    open fun area(): Float {
        return length * width    
    }
}

The open keyword prefixing a function indicates that the function can be overridden.

In this example, the Shape class has two methods: perimeter and area. You can have another class, Circle, which inherits from the Shape class:

class Circle(diameter:Float):    
    Shape(diameter / 2, diameter / 2) {

    override fun area():Float {
        return (Math.PI * this.length.pow(2F)).toFloat()    
    }

    override fun perimeter(): Float {
        return (2 * Math.PI * this.length).toFloat()    
    }

    // overloading    
    fun perimeter(radius:Float): Float {
        return (2 * Math.PI * radius).toFloat()    
    }
}

Here, the Circle class overrides the two methods in the base class: area() and perimeter(). At the same time, it also overloads the perimeter() method.

var c = Circle(6F)

// area() overrides the one in Shape
val area = c.area()             // 28.274334

// perimeter() is overloaded
var perimeter = c.perimeter()   // 18.849556
perimeter = c.perimeter(5F)     // 31.415926

Data Class

One new feature in Kotlin is the concept of a data class. Data class was invented because often, it's necessary to create classes whose only purpose is to hold data. For such a purpose, you can create a data class using the data keyword:

data class Go (    
    var row:Int,    
    var column:Int
)

A data class is like a regular class, but:

  • It doesn't have methods, only properties.
  • There's no need to create setters and getters.
  • There must be at least one primary constructor parameter.

You use a data class just like a regular class:

var stone1 = Go(12,16)
with(stone1) { 
   row = 9 
   column = 13
}

What is interesting with data class is that the toString() method is automatically implemented for you:

print(stone1.toString())
// Go(row=9, column=13)

Also, a data class has the componentN methods automatically generated for you:

print(stone1.component1().toString()) // 9
print(stone1.component2().toString()) // 13

Identity Operator

In Kotlin, there are two types of equality checks:

  • Structural Equality: You use the == operator to compare the data of two references.
  • Referential Equality: You use the === operator to compare to see if two objects point to the same instance.

Consider the following MyPointClass:

class MyPointClass {    
    var x = 0    
    var y = 0    
    var radius = 0    

    constructor(x:Int, y:Int, radius:Int) {        
        this.x = x        
        this.y = y    
    }
    
    override fun equals(other: Any?): Boolean {        
        other as MyPointClass        
        return this.x == other.x &&
        this.y == other.y    
    }
}

You've overridden the equals() function to define how two objects of type MyPointClass are considered structurally equal. In this example, two objects are considered equal as long as the x and y properties are equal in value.

You can now test a number of objects for structural equality and referential equality:

var pt1 = MyPointClass(5,6,7)
var pt2 = pt1
var pt3 = MyPointClass(5,6,7)
var pt4 = MyPointClass(5,7,9)
var pt5 = MyPointClass(5,6,2)
var result:Boolean

// structural equality
result = pt1 == pt2  // true
result = pt1 == pt4  // false
result = pt1 == pt3  // true
result = pt1 == pt5  // true

// referential equality      
result = pt1 === pt2 // true
result = pt1 === pt3 // false

Interfaces

To define an abstract class in Kotlin, you use interfaces. Interfaces allow you to define not only abstract classes, but also provide default implementation for methods. Consider the following interface, CarInterface:

interface CarInterface {
    fun accelerate() {
        // default implementation
    }  
    fun decelerate()    
    fun accelerateBy(amount:Int)
}

It contains three methods, one of which has a default implementation. A class that implements this interface must provide the implementation for the two methods (which are abstract), and optionally override the method that has the default implementation:

class MyCar:CarInterface {    
    // optional    
    override fun accelerate() {
    // override implementation in interface    
    }    
    override fun decelerate() {
    }    
    override fun accelerateBy(amount:Int) {
    }
}

It's important to remember that in Kotlin, interface cannot maintain state. For example, you want to define a speed property in the CarInterface like this:

interface CarInterface {    
    var speed: Int    
    
    fun accelerate() {        
        // default implementation    
    }
    fun decelerate()    
    fun accelerateBy(amount:Int)
}

Observe that you aren't allowed to assign a value to the speed property in the interface. You can only assign it in the implementation:

class MyCar:CarInterface {    
    override var speed = 0    
    
    // optional   
    override fun accelerate() {        
    // override implementation in interface  
    }   
    override fun decelerate() { 
    }
    override fun accelerateBy(amount:Int) {    
    }
}

Extensions

Kotlin provides the ability for you to easily extend a class with new functionality without needing to inherit the class. To see the usefulness of extensions in Kotlin, consider the following statement, one with which most Android developers are very familiar:

// without extension     
Toast.makeText(this, "Hello, Kotlin", Toast.LENGTH_SHORT).show()

Every time you want to display a toast you need to write this long statement. It would be very handy if you could shorten this to a simple function call. With extension, you can:

fun Context.displayToast(   
    text:CharSequence,  
    duration:Int= Toast.LENGTH_SHORT) { 
    Toast.makeText(this, text, duration).show()
}

The above statements extend the Context class with the displayToast() function. To display a toast, you now can simply call the displayToast() function in your activity, like this:

displayToast("Hello, Kotlin!")

Here's another example. Suppose that you often store the latitude and longitude of a location as a string in the following format “lat,lng”, for example, “1.23456,103.345678”. In this case, it would be very useful to have an extension method added to the String class where you could extract the latitude and longitude from a string directly. To do that, you can add an extension named LatLng to the String class like this:

fun String.LatLng(splitter:String): Pair<Float, Float> {
    val latlng = this.split(splitter)
    return Pair(latlng[0].toFloat(),
    latlng[1].toFloat())
}

You can now extract the latitude and longitude of a location stored as a string like this:

var str = "1.23456,103.345678"
var latlng  = str.LatLng(",")
print(latlng.first)  // 1.23456
print(latlng.second) // 103.345678

Generic Class

As in most modern programming languages, Kotlin supports generic classes. Generic classes contain operations that aren't specific to a particular data type. They contain a placeholder type that must be filled in whenever the class or function is used.

Generic classes greatly enhance code reusability.

Here's an example of a generic implementation of a stack class:

class MyStack<T> {    
    val elements:MutableList<T> = mutableListOf()
    fun push(item:T) {
    elements.add(item)    
    }
    fun pop():T? {
        if (elements.size>0) {
            return elements.removeAt(elements.size-1)        
        } else {
            return null
        }
    }
}

In this implementation, the data type that this stack implementation works with is not specified. Rather, it's represented by the placeholder – T.

To use this stack implementation with the String type, create an instance of the MyStack class and pass it the String type, as follows:

val myStringStack = MyStack<String>()
myStringStack.push("programming")
myStringStack.push("Kotlin")
print(myStringStack.pop()) // Kotlin
print(myStringStack.pop()) // programming

Likewise, you can use the MyStack class with the Int type:

val myIntStack = MyStack<Int>()
myIntStack.push(5)
myIntStack.push(6)
print(myIntStack.pop().toString()) // 6
print(myIntStack.pop().toString()) // 5

Functional Programming

Functional programming is all the rage these days and Kotlin is part of the movement. Functional programming is a programming paradigm that treats computation as the evaluation of mathematical functions without any side-effects. With functional programming, use expressions to express what you want done, not how to do it.

Lambda Functions

Lambda functions are essentially anonymous functions, and you treat them just like you treat your variables. For example, you can pass Lambda functions as arguments to methods or return them as return values.

To understand Lambda functions in Kotlin, here's an example that most Android developers are familiar with. Suppose I have a button in my user interface with the ID btnOpen. To handle the button's click event, I do something like this:

//---Button view--- 
val btnOpen = findViewById<Button>(R.id.btnOpen)

// traditional way
btnOpen.setOnClickListener(object: View.OnClickListener{    
    override fun onClick(v: View?) {
    //...    
    }}
)

And by the way, in Kotlin, you no longer need to do the following in your Android application:

//---Button view---            
val btnOpen = findViewById<Button>(R.id.btnOpen)

This is due to the Kotlin Android Extensions plug-in that's automatically installed in your Android project. This plugin includes a view binder that automatically generates code giving you direct access to views in the layout XML without needing to explicitly find them using findViewById().

Using the Lambda function, the onClick() function could be replaced:

// alternative 1      
btnOpen.setOnClickListener(
{ view ->    
//...
})

And because the Lambda function is the last argument of the setOnClickListener() function, you could close the parentheses, like this:

// alternative 2      btnOpen.setOnClickListener()
{ view ->   
    // ...
}

Because there are no other arguments needed to pass to the setOnClickListener() function other than the Lambda function, you can omit the parentheses:

// alternative 3    
btnOpen.setOnClickListener { view ->    
// ...
}

If you have no use for the view argument in the Lambda function, you can also omit it entirely:

// alternative 4      
btnOpen.setOnClickListener {    
// ...
}

Earlier on, you learned about the arrays in Kotlin. Arrays also supports Lambda functions. Here are some examples:

var numbers = arrayOf(5,2,8,7,9,4,3,1)

// return all even numbers
val evenNumbers = numbers.filter {
    n -> n % 2 == 0
}

// sum up all the numbers
val sums = numbers.reduce {
    sum, n -> sum + n
}

// convert the list to strings, prefix each item with "$"
val prices = numbers.map{
    n -> "\$$n"
}

// apply GST (Goods and Services Tax) to items above 3,
// convert the list to strings, prefix with "$"
val pricesWithGST = numbers.map{
    n -> "$" + if (n>3) (n*1.07) else n
}

// sort in ascending order
val sortedASCNumbers = numbers.sortedBy {
  it
}
  
// sort in descending order
val sortedDESCNumbers = numbers.sortedBy {   
    it
}.reversed()

Accepting Lambda Functions as Arguments in Your Functions

Supplying a Lambda function to a function is straightforward, but how about accepting a Lambda function in a function? Let me illustrate this with an example. Suppose you want to implement a simple bubble sort function. And you want the user of this function to be able to specify sorting in ascending or descending order. This is a good situation to see the Lambda function in action. Define the bubbleSort() function as follows:

fun bubbleSort(items:IntArray,  
    compareFun:(Int, Int) -> Boolean):IntArray {
    
    for (j in 0..items.size-2) {    
        var swapped = false 
        for (i in 0..items.size-2-j) {
        // you need to swap if the numbers are not in order 
            if (!compareFun(items[i],items[i+1])) {     
                val temp = items[i+1]
                items[i+1] = items[i]  
                items[i] = temp
                swapped = true
            }
        }    
        if (!swapped) break
    }
    return items
}

Note that for the second parameter of the function, it takes in a function with the following signature and call it compareFun:

compareFun:(Int, Int) -> Boolean)

In the body of the bubbleSort() function, you then call this function and, based on its result, determine if two numbers need to be swapped:

if (!compareFun(items[i], items[i+1])) { 
    val temp = items[i+1] 
    items[i+1] = items[i]
    items[i] = temp
    swapped = true
}

Let's try the function now. The following example sorts an array in ascending order:

val numbers = intArrayOf(5,2,8,7,9,4,3,1)
// ascending order
var sortedNumbers = bubbleSort(numbers, {
    num1, num2 -> num1 < num2    
})

Essentially, the caller of the bubbleSort() function passes in a Lambda function to determine how the sorting should go.

And to sort in descending order:

// descending order
sortedNumbers = bubbleSort(numbers, {
    num1, num2 -> num1 > num2
})

Remember that if the Lambda function is the last argument in a function, it can be outside the parentheses:

// another way of passing in the lambda function    
sortedNumbers = bubbleSort(numbers) {
    num1, num2 -> num1 < num2 
}

Summary

I hope I've covered enough for you to get started with Kotlin programming. Although it isn't possible to cover all aspects of a language in a single article, I hope this article has motivated you to start writing your Android apps in Kotlin. And as the saying goes: practice makes perfect.