Tagged: Kotlin

Kotlin Type Checks and Smart Casts
07
Mar
2021

Kotlin Type Checks and Smart Casts

When working with mixed types, We often need to know the type of an object at runtime so that we can safely cast the object to our desired type and call methods or access properties on it.

Type Checks

In Kotlin, You can check whether an object is of a certain type at runtime by using the is operator.

Following is an example that demonstrates the usage of is operator.

fun main(args: Array<String>) {
    val mixedTypeList: List<Any> = listOf("I", "am", 5, "feet", 9.5, "inches", "tall")

    for(value in mixedTypeList) {
        if (value is String) {
            println("String: '$value' of length ${value.length} ")
        } else if (value is Int) {
            println("Integer: '$value'")
        } else if (value is Double) {
            println("Double: '$value' with Ceil value ${Math.ceil(value)}")
        } else {
            println("Unknown Type")
        }
    }
}

In the example above, We have a list containing mixed types. We iterate through the list, check the type of each value in the list using the is operator and print the details about the value.

Following is the output of the above program –

# Output
String: 'I' of length 1 
String: 'am' of length 2 
Integer: 5
String: 'feet' of length 4 
Double: 9.5 with Ceil value 10.0
String: 'inches' of length 6 
String: 'tall' of length 4 

Note that you can simplify the above program further by replacing the if-else block with a when expression like this –

for(value in mixedTypeList) {
    when(value) {
        is String -> println("String: '$value' of length ${value.length} ")
        is Int -> println("Integer: $value")
        is Double -> println("Double: $value with Ceil value ${Math.ceil(value)}")
        else -> println("Unknown Type")
    }
}

The is operator also has a negated form !is. Here is an example of !is operator –

if(value !is String) {
    println("Not a String")
}

Smart Casts

The examples described in the previous section uses a feature of Kotlin called Smart Cast. To understand how Smart Cast work in Kotlin, Let’s compare how we do class casting in Java vs Kotlin.

In Java, We first check the type of the variable using the instanceof operator and then cast it to the target type like this –

Object obj = "The quick brown fox jumped over a lazy dog";
if(obj instanceof String) {
    // Explicit Casting to `String`
    String str = (String) obj;
    System.out.println("Found a String of length " + str.length());
}

But In Kotlin, When you perform an is or !is check on a variable, the compiler tracks this information and automatically casts the variable to the target type in the scope where the is or !is check is true.

val obj: Any = "The quick brown fox jumped over a lazy dog"
if(obj is String) {
    // The variable obj is automatically cast to a String in this scope.
    // No Explicit Casting needed. 
    println("Found a String of length ${obj.length}")
}

Similarly, for !is check –

val obj: Any = "The quick brown fox jumped over a lazy dog"
if(obj !is String) {
    println("Not a String")
} else {
    // obj is automatically cast to a String in this scope
    println("Found a String of length ${obj.length}")
}

That’s not all, Smart Casts also work with Short-Circuit operators && and || –

/* 
   obj is automatically cast to String on the right-hand side 
   of "&&" and in the "if" branch
*/
if(obj is String && obj.length > 0) {
    println("Found a String of length greater than zero - ${obj.length}")
}
// obj is automatically cast to String on the right-hand side of "||"
if(obj !is String || obj.length > 0) {
    return
}

Note that Smart Casts work only if the compiler can guarantee that the variable hasn’t changed after the is or !is check.

For example, Smart cast doesn’t work for mutable properties of a class. It works only for immutable properties that don’t have a custom getter.

Explicit Casting

1. Unsafe Cast Operator: as

You can use Kotlin’s Type Cast Operator as to manually cast a variable to a target type –

val obj: Any = "The quick brown fox jumped over a lazy dog"
val str: String = obj as String
println(str.length)

If the variable can’t be cast to the target type then the cast operator throws an exception. That’s why we call the as operator “Unsafe” –

val obj: Any = 123
val str: String = obj as String 
// Throws java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

Note that if the variable that you’re trying to cast is nullable then you can’t cast it to a non-null type –

val obj: Any? = null
val str: String = obj as String
// Throws kotlin.TypeCastException: null cannot be cast to non-null type kotlin.String

The target type needs to be nullable as well for the casting to work –

val obj: Any? = null
val str: String? = obj as String? // Works
println(str) // Prints null

2. Safe Cast operator: as?

As you learned in the previous section, the type cast operator as throws ClassCastException at runtime if the casting is not possible.

Kotlin also provides a Safe cast operator as? that returns null instead of throwing a ClassCastException if the casting is not possible –

val obj: Any = 123
val str: String? = obj as? String // Works
println(str)  // Prints null

Type Check & Smart Cast Example with User Defined Classes and Inheritance

The following example demonstrates Kotlin’s Type Check and Smart Cast concepts using User Defined Classes and inheritance –

open class Animal

class Cat : Animal() {
    fun meow() {
        println("Meow Meow Meow...")
    }
}

class Dog: Animal() {
    fun bark() {
        println("Woof Woof Woof...")
    }
}
fun main(args: Array<String>) {
    val animal: Animal = Cat()

    if(animal is Cat) {
    	// No explicit casting needed to `Cat`
        println(animal.meow())
    } else if (animal is Dog) {
    	// No explicit casting needed to `Dog`
        println(animal.bark())
    }
}
# Output
Meow Meow Meow...

Conclusion

That’s all folks! In this article, You learned how Type Checks and Smart Casts work in Kotlin. You also learned how to use Unsafe and Safe Type Cast Operators for explicitly casting a variable to a target type.

Introduction to Data Classes in Kotlin
07
Mar
2021

Introduction to Data Classes in Kotlin

While building any application, we often need to create classes whose primary purpose is to hold data/state. These classes generally contain the same old boilerplate code in the form of getterssettersequals()hashcode() and toString() methods.

Motivation

Consider the following example of a Customer class in Java that just holds data about a Customer and doesn’t have any functionality whatsoever –

public class Customer {
    private String id;
    private String name;

    public Customer(String id, String name) {
        this.id = id;
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Customer customer = (Customer) o;

        if (id != null ? !id.equals(customer.id) : customer.id != null) return false;
        return name != null ? name.equals(customer.name) : customer.name == null;
    }

    @Override
    public int hashCode() {
        int result = id != null ? id.hashCode() : 0;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        return result;
    }
}

You see, for creating a Simple class with only two member fields, we had to write almost 50 lines of code.

Yes, I know that you don’t need to write that code yourself and any good IDE can generate all that boilerplate code for you.

But that code will still be there in your source file and clutter it. Moreover, whenever you add a new member field to the Class, you’ll need to regenerate/modify the constructors, getters/setters and equals()/hashcode() methods.

You can also use a third party library like Project Lombok to generate getters/settersequals()/hashCode()toString() methods and more. But there is no out of the box solution without any library that can help us avoid these boilerplate codes in our application.

Kotlin Data Classes

Kotlin has a better solution for classes that are used to hold data/state. It’s called a Data Class. A Data Class is like a regular class but with some additional functionalities.

With Kotlin’s data classes, you don’t need to write/generate all the lengthy boilerplate code yourself. The compiler automatically generates a default getter and setter for all the mutable properties, and a getter (only) for all the read-only properties of the data class. Moreover, It also derives the implementation of standard methods like equals()hashCode() and toString() from the properties declared in the data class’s primary constructor.

For example, The Customer class that we wrote in the previous section in Java can be written in Kotlin in just one line –

data class Customer(val id: Long, val name: String)

Accessing the properties of the data class

The following example shows how you can access the properties of the data class –

val customer = Customer(1, "Sachin")

// Getting a property
val name = customer.name

Since all the properties of the Customer class are immutable, there is no default setter generated by the compiler. Therefore, If you try to set a property, the compiler will give an error –

// Setting a Property

// You cannot set read-only properties
customer.id = 2	// Error: Val cannot be assigned

Let’s now see how we can use the equals()hashCode(), and toString() methods of the data class-

1. Data class’s equals() method

val customer1 = Customer(1, "John")
val customer2 = Customer(1, "John")

println(customer1.equals(customer2))  // Prints true

You can also use Kotlin’s Structural equality operator == to check for equality. The == operator internally calls the equals() method –

println(customer1 == customer2)  // Prints true

2. Data class’s toString() method

The toString() method converts the object to a String in the form of "ClassName(field1=value1, field2=value)" –

val customer = Customer(2, "Robert")
println("Customer Details : $customer")
# Output
Customer Details : Customer(id=2, name=Robert)

3. Data class’s hashCode() method

val customer = Customer(2, "Robert")
println("Customer HashCode : ${customer.hashCode()}") // Prints -1841845792

Apart from the standard methods like equals()hashCode() and toString(), Kotlin also generates a copy() function and componentN() functions for all the data classes. Let’s understand what these functions do and how to use them –

Data Classes and Immutability: The copy() function

Although the properties of a data class can be mutable (declared using var), It’s strongly recommended to use immutable properties (declared using val) so as to keep the instances of the data class immutable.

Immutable objects are easier to work with and reason about while working with multi-threaded applications. Since they can not be modified after creation, you don’t need to worry about concurrency issues that arise when multiple threads try to modify an object at the same time.

Kotlin makes working with immutable data objects easier by automatically generating a copy() function for all the data classes. You can use the copy() function to copy an existing object into a new object and modify some of the properties while keeping the existing object unchanged.

The following example shows how copy() function can be used –

val customer = Customer(3, "James")

/* 
   Copies the customer object into a separate Object and updates the name. 
   The existing customer object remains unchanged.
*/
val updatedCustomer = customer.copy(name = "James Altucher")
println("Customer : $customer")
println("Updated Customer : $updatedCustomer")
# Output
Customer : Customer(id=3, name=James)
Updated Customer : Customer(id=3, name=James Altucher)

Data Classes and Destructuring Declarations: The componentN() functions

Kotlin also generates componentN() functions corresponding to all the properties declared in the primary constructor of the data class.

For the Customer data class that we defined in the previous section, Kotlin generates two componentN() functions – component1() and component2() corresponding to the id and name properties –

val customer = Customer(4, "Joseph")

println(customer.component1()) // Prints 4
println(customer.component2()) // Prints "Joseph"

The component functions enable us to use the so-called Destructuring Declaration in Kotlin. The Destructuring declaration syntax helps you destructure an object into a number of variables like this –

val customer = Customer(4, "Joseph")

// Destructuring Declaration
val (id, name) = customer
println("id = $id, name = $name") // Prints "id = 4, name = Joseph"

Requirements for Data Classes

Every Data Class in Kotlin needs to fulfill the following requirements –

  • The primary constructor must have at least one parameter
  • All the parameters declared in the primary constructor need to be marked as val or var.
  • Data classes cannot be abstract, open, sealed or inner.

Conclusion

Data classes help us avoid a lot of common boilerplate code and make the classes clean and concise. In this article, you learned how data classes work and how to use them. I hope you understood the all the concepts presented in this article.

Kotlin Abstract Classes with Examples
07
Mar
2021

Kotlin Abstract Classes with Examples

An abstract class is a class that cannot be instantiated. We create abstract classes to provide a common template for other classes to extend and use.

Declaring an Abstract Class

You can declare an abstract class using the abstract keyword –

abstract class Vehicle

An abstract class may contain both abstract and non-abstract properties and functions. You need to explicitly use the abstract keyword to declare a property or function as abstract –

abstract class Vehicle(val name: String,
                       val color: String,
                       val weight: Double) {   // Concrete (Non Abstract) Properties

    // Abstract Property (Must be overridden by Subclasses)
    abstract var maxSpeed: Double

    // Abstract Methods (Must be implemented by Subclasses)
    abstract fun start()
    abstract fun stop()

    // Concrete (Non Abstract) Method
    fun displayDetails() {
        println("Name: $name, Color: $color, Weight: $weight, Max Speed: $maxSpeed")
    }
}

Any subclass that extends the abstract class must implement all of its abstract methods and properties, or the subclass should also be declared as abstract.

If you recall from the Kotlin Inheritance tutorial, you need to annotate a class as open to allow other classes to inherit from it. But, you don’t need to do that with abstract classes. Abstract classes are open for extension by default.

Similarly, abstract methods and properties are open for overriding by default.

But, If you need to override a non-abstract method or property, then you must mark it with the open modifier.

Extending from an Abstract class

Following are two concrete classes that extend the Vehicle abstract class and override its abstract methods and properties –

class Car(name: String,
          color: String,
          weight: Double,
          override var maxSpeed: Double): Vehicle(name, color, weight) {

    override fun start() {
        // Code to start a Car
        println("Car Started")
    }

    override fun stop() {
        // Code to stop a Car
        println("Car Stopped")
    }
}
class Motorcycle(name: String,
           color: String,
           weight: Double,
           override var maxSpeed: Double): Vehicle(name, color, weight) {

    override fun start() {
        // Code to Start the Motorcycle
        println("Bike Started")
    }

    override fun stop() {
        // Code to Stop the Motorcycle
        println("Bike Stopped")
    }
}

Let’s now write some code to test our abstract and concrete classes in the main method –

fun main(args: Array<String>) {

    val car = Car("Ferrari 812 Superfast", "red", 1525.0, 339.60)
    val motorCycle = Motorcycle("Ducati 1098s", "red", 173.0, 271.0)

    car.displayDetails()
    motorCycle.displayDetails()

    car.start()
    motorCycle.start()
}

Here is the output of the above main() method –

# Output
Name: Ferrari 812 Superfast, Color: red, Weight: 1525.0, Max Speed: 339.6
Name: Ducati 1098s, Color: red, Weight: 173.0, Max Speed: 271.0
Car Started
Bike Started

Conclusion

Abstract classes help you abstract out common functionality into a base class. It may contain both abstract and non-abstract properties and methods. An abstract class is useless on its own because you cannot create objects from it. But, other concrete (non-abstract) classes can extend it and build upon it to provide the desired functionality.

Kotlin Inheritance, Method Overriding, and Property Overriding
07
Mar
2021

Kotlin Inheritance, Method Overriding, and Property Overriding

Inheritance is one of the key concepts of Object Oriented Programming (OOP). Inheritance enables re-usability. It allows a class to inherit features (properties and methods) from another class.

The class that inherits the features of another class is called the Child class or Derived class or Sub class, and the class whose features are inherited is called the Parent class or Base class or Super class.

All the classes in Kotlin have a common base class called Any. It corresponds to the Object class in Java. Every class that you create in Kotlin implicitly inherits from Any –

class Person // Implicitly inherits from the default Super class - Any

The Any class contains three methods namely equals()hashCode() and toString(). All the classes in Kotlin inherit these three methods from Any, and can override them to provide their own implementation.

Inheritance (Creating Base and Derived classes)

Here is how you declare a base class and a derived class in Kotlin –

// Base class (Super class)
open class Computer {
}

// Derived class (Sub class)
class Laptop: Computer() {
}

Notice the use of open keyword in the base class. By default, all the classes in Kotlin are final (non-inheritable).

To allow a class to be inherited by others, you must mark it with the open modifier.

Note that the child class has the responsibility to initialize the parent class. If the child class has a primary constructor, then it must initialize the parent class right in the class header with the parameters passed to its primary constructor –

// Parent class
open class Computer(val name: String,
                    val brand: String) {
}

// Child class (initializes the parent class)
class Laptop(name: String, 
             brand: String, 
             val batteryLife: Double) : Computer(name, brand) {
   
}

If the child class doesn’t have a primary constructor, then all of its secondary constructors have to initialize the parent class either by calling the super keyword directly or by delegating to another constructor that does that –

class Laptop : Computer {
    val batteryLife: Double

	// Calls super() to initialize the Parent class
    constructor(name: String, brand: String, batteryLife: Double): super(name, brand) {
        this.batteryLife = batteryLife
    }

	// Calls another constructor (which calls super())
    constructor(name: String, brand: String): this(name, brand, 0.0) {
        
    }
}

In the above examples, we initialized the parent class using its primary constructor. If the parent class contains one or more secondary constructors, then the child class can initialize the parent class using any of the primary constructor or secondary constructors.

Just keep in mind that the parent class needs to be initialized. It doesn’t matter which of its constructor is used to initialize it.

Inheritance Example with Properties and Member Functions

Let’s now see a complete example of Inheritance in Kotlin. Consider a banking application where people can have several types of Bank accounts like SavingsAccount, CurrentAccount etc.

In such cases, it makes sense to create a base class called BankAccount and let other classes like SavingsAccount and CurrentAccount inherit from the BankAccount class.

Following is a simple BankAccount class for our Banking application –

/**
 * BankAccount (Base Class)
 * @property accountNumber - Account Number (read-only)
 * @property accountName -  Account Name (read-only)
 * @property balance - Current Balance (Mutable)
 */

open class BankAccount(val accountNumber: String, val accountName: String) {
    var balance : Double = 0.0

    fun depositeMoney(amount: Double): Boolean {
        if(amount > 0) {
            balance += amount
            return true
        } else {
            return false
        }
    }

    fun withdrawMoney(amount: Double): Boolean {
        if(amount > balance) {
            return false
        } else {
            balance -= amount
            return true
        }
    }

}

A Savings account is a Bank account with some interest rate on the balance amount. We can model the SavingsAccount class in the following way –

/**
 * SavingsAccount (Derived Class)
 * @property interestRate - Interest Rate for SavingsAccount (read-only)
 * @constructor - Primary constructor for creating a Savings Account
 * @param accountNumber - Account Number (used to initialize BankAccount)
 * @param accountName - Account Name (used to initialize BankAccount)
 */

class SavingsAccount (accountNumber: String, accountName: String, val interestRate: Double) :
        BankAccount(accountNumber, accountName) {

    fun depositInterest() {
        val interest = balance * interestRate / 100
        this.depositeMoney(interest);
    }
}

The SavingsAccount class inherits the following features from the base class –

  • Properties – accountNumberaccountNamebalance
  • Methods – depositMoneywithdrawMoney

Let’s now write some code to test the above classes and methods –

fun main(args: Array<String>) {
    // Create a Savings Account with 6% interest rate
    val savingsAccount = SavingsAccount("64524627", "Rajeev Kumar Singh", 6.0)
    
    savingsAccount.depositeMoney(1000.0)
    
    savingsAccount.depositInterest()
    
    println("Current Balance = ${savingsAccount.balance}")
}

Overriding Member Functions

Just like Kotlin classes, members of a Kotlin class are also final by default. To allow a member function to be overridden, you need to mark it with the open modifier.

Moreover, The derived class that overrides a base class function must use the override modifier, otherwise, the compiler will generate an error –

open class Teacher {
    // Must use "open" modifier to allow child classes to override it
    open fun teach() {
        println("Teaching...")
    }
}

class MathsTeacher : Teacher() {
    // Must use "override" modifier to override a base class function
    override fun teach() {
        println("Teaching Maths...")
    }
}

Let’s test the above classes by defining the main method –

fun main(args: Array<String>) {
    val teacher = Teacher()
    val mathsTeacher = MathsTeacher()

    teacher.teach()  // Teaching...
    mathsTeacher.teach() // Teaching Maths..
}

Dynamic Polymorphism

Polymorphism is an important concept in Object Oriented Programming. There are two types of polymorphism –

  1. Static (compile-time) Polymorphism
  2. Dynamic (run-time) Polymorphism

Static polymorphism occurs when you define multiple overloaded functions with same name but different signatures. It is called compile-time polymorphism because the compiler can decide which function to call at compile itself.

Dynamic polymorphism occurs in case of function overriding. In this case, the function that is called is decided at run-time.

Here is an example –

fun main(args: Array<String>) {
    val teacher1: Teacher = Teacher()  // Teacher reference and object
    val teacher2: Teacher = MathsTeacher() // Teacher reference but MathsTeacher object

    teacher1.teach()  // Teaching...
    teacher2.teach() // Teaching Maths..
}

The line teacher2.teach() calls teach() function of MathsTeacher class even if teacher2 is of type Teacher. This is because teacher2 refers to a MathsTeacher object.

Overriding Properties

Just like functions, you can override the properties of a super class as well. To allow child classes to override a property of a parent class, you must annotate it with the open modifier.

Moreover, The child class must use override keyword for overriding a property of a parent class –

open class Employee {
    // Use "open" modifier to allow child classes to override this property
    open val baseSalary: Double = 30000.0
}

class Programmer : Employee() {
    // Use "override" modifier to override the property of base class
    override val baseSalary: Double = 50000.0
}

fun main(args: Array<String>) {
    val employee = Employee()
    println(employee.baseSalary) // 30000.0

    val programmer = Programmer()
    println(programmer.baseSalary) // 50000.0
}

Overriding Property’s Getter/Setter method

You can override a super class property either using an initializer or using a custom getter/setter.

In the example below, we’re overriding the age property by defining a custom setter method –

open class Person {
    open var age: Int = 1
}

class CheckedPerson: Person() {
    override var age: Int = 1
        set(value) {
            field = if(value > 0) value else throw IllegalArgumentException("Age can not be negative")
        }
}

fun main(args: Array<String>) {
    val person = Person()
    person.age = -5 // Works

    val checkedPerson = CheckedPerson()
    checkedPerson.age = -5  // Throws IllegalArgumentException : Age can not be negative
}

Calling properties and functions of Super class

When you override a property or a member function of a super class, the super class implementation is shadowed by the child class implementation.

You can access the properties and functions of the super class using super() keyword.

Here is an example –

open class Employee {
    open val baseSalary: Double = 10000.0

    open fun displayDetails() {
        println("I am an Employee")
    }
}

class Developer: Employee() {
    override var baseSalary: Double = super.baseSalary + 10000.0

    override fun displayDetails() {
        super.displayDetails()
        println("I am a Developer")
    }
}

Conclusion

That’s all in this article folks. I hope you understood how inheritance works in Kotlin

Kotlin Properties, Backing Fields, Getters and Setters
07
Mar
2021

Kotlin Properties, Backing Fields, Getters and Setters

You can declare properties inside Kotlin classes in the same way you declare any other variable. These properties can be mutable (declared using var) or immutable (declared using val).

Here is an example –

// User class with a Primary constructor that accepts three parameters
class User(_id: Int, _name: String, _age: Int) {
    // Properties of User class
    val id: Int = _id         // Immutable (Read only)
    var name: String = _name  // Mutable
    var age: Int = _age       // Mutable
}

You can get or set the properties of an object using the dot(.) notation like so –

val user = User(1, "Jack Sparrow", 44)

// Getting a Property
val name = user.name

// Setting a Property
user.age = 46

// You cannot set read-only properties
user.id = 2	// Error: Val cannot be assigned

Getters and Setters

Kotlin internally generates a default getter and setter for mutable properties, and a getter (only) for read-only properties.

It calls these getters and setters internally whenever you access or modify a property using the dot(.) notation.

Here is how the User class that we saw in the previous section looks like with the default getters and setters –

class User(_id: Int, _name: String, _age: Int) {
    val id: Int = _id
        get() = field
    
    var name: String = _name
        get() = field
        set(value) {
            field = value
        }
    
    var age: Int = _age
        get() = field
        set(value) {
            field = value
        }
}

You might have noticed two strange identifiers in all the getter and setter methods – field and value.

Let’s understand what these identifiers are for –

1. value

We use value as the name of the setter parameter. This is the default convention in Kotlin but you’re free to use any other name if you want.

The value parameter contains the value that a property is assigned to. For example, when you write user.name = "Bill Gates", the value parameter contains the assigned value “Bill Gates”.

2. Backing Field (field)

Backing field helps you refer to the property inside the getter and setter methods. This is required because if you use the property directly inside the getter or setter then you’ll run into a recursive call which will generate a StackOverflowError.

Let’s see that in action. Let’s modify the User class and refer to the properties directly inside the getters and setters instead of using the field identifier –

class User(_name: String, _age: Int) {
    var name: String = _name
        get() = name		  // Calls the getter recursively

    var age: Int = _age
        set(value) {
            age = value		  // Calls the setter recursively
        }
}

fun main(args: Array<String>) {
    val user = User("Jack Sparrow", 44)

    // Getting a Property
    println("${user.name}") // StackOverflowError

    // Setting a Property
    user.age = 45           // StackOverflowError

}

The above program will generate a StackOverflowError due to the recursive calls in the getter and setter methods. This is why Kotlin has the concept of backing fields. It makes storing the property value in memory possible. When you initialize a property with a value in the class, the initializer value is written to the backing field of that property –

class User(_id: Int) {
    val id: Int	= _id   // The initializer value is written to the backing field of the property
}

A backing field is generated for a property if

  • A custom getter/setter references it through the field identifier or,
  • It uses the default implementation of at least one of the accessors (getter or setter). (Remember, the default getter and setter references the field identifier themselves)

For example, a backing field is not generated for the id property in the following class because it neither uses the default implementation of the getter nor refers to the field identifier in the custom getter –

class User() {
    val id	// No backing field is generated
        get() = 0
}

Since a backing field is not generated, you won’t be able to initialize the above property like so –

class User(_id: Int) {
    val id: Int = _id	//Error: Initialization not possible because no backing field is generated which could store the initialized value in memory
        get() = 0
}

Creating Custom Getters and Setters

You can ditch Kotlin’s default getter/setter and define a custom getter and setter for the properties of your class.

Here is an example –

class User(_id: Int, _name: String, _age: Int) {
    val id: Int = _id

    var name: String = _name 
        // Custom Getter
        get() {     
            return field.toUpperCase()
        }     

    var age: Int = _age
        // Custom Setter
        set(value) {
            field = if(value > 0) value else throw IllegalArgumentException("Age must be greater than zero")
        }
}

fun main(args: Array<String>) {
    val user = User(1, "Jack Sparrow", 44)

    println("${user.name}") // JACK SPARROW

    user.age = -1           // Throws IllegalArgumentException: Age must be greater that zero
}

Conclusion

Thanks for reading folks. In this article, You learned how Kotlin’s getters and setters work and how to define a custom getter and setter for the properties of a class.

Kotlin Infix Notation - Make function calls more intuitive
07
Mar
2021

Kotlin Classes, Objects, Constructors and Initializers

Kotlin supports method calls of a special kind, called infix calls.

You can mark any member function or extension function with the infix modifier to allow it to be called using infix notation. The only requirement is that the function should have only one required parameter.

Infix notations are used extensively in Kotlin. If you’ve been programming in Kotlin, chances are that you’ve already used infix notations.

Following are few common examples of infix notations in Kotlin –

1. Infix Notation Example – Creating a Map

val map = mapOf(1 to "one", 2 to "two", 3 to "three")

In the above example, the expressions 1 to "one"2 to "two" etc, are infix notations of the function calls 1.to("one") and 2.to("two") etc.

to() is an infix function that creates a Pair<A, B> from two values.

2. Infix Notation Example – Range Operators (until, downTo, step)

Kotlin provides various range operators that are usually called using infix notation –

for(i in 1 until 10) {	// Same as - for(i in 1.until(10))
    print("$i ")
}
for(i in 10 downTo 1) {	 // Same as - for(i in 10.downTo(1))
    print("$i ")
}
for(i in 1 until 10 step 2) { // Same as - for(i in 1.until(10).step(2))
    print("$i ")
}

3. Infix Notation Example – String.matches()

The String.matches() function in Kotlin which matches a String with a Regex is an infix function –

val regex = Regex("[tT]rue|[yY]es")
val str = "yes"

str.matches(regex)

// Infix notation of the above function call -
str matches regex

Creating an Infix Function

You can make a single argument member function or extension function, an infix function by marking it with the infix keyword.

Check out the following example where I have created an infix member function called add() for adding two Complex numbers –

data class ComplexNumber(val realPart: Double, val imaginaryPart: Double) {
	// Infix function for adding two complex numbers
    infix fun add(c: ComplexNumber): ComplexNumber {
        return ComplexNumber(realPart + c.realPart, imaginaryPart + c.imaginaryPart)
    }
}

You can now call the add() method using infix notation –

val c1 = ComplexNumber(3.0, 5.0)
val c2 = ComplexNumber(4.0, 7.0)

// Usual call
c1.add(c2) // produces - ComplexNumber(realPart=7.0, imaginaryPart=12.0)

// Infix call
c1 add c2  // produces - ComplexNumber(realPart=7.0, imaginaryPart=12.0)

Conclusion

That’s all folks. In this article, You learned what infix notation is and how it works. You saw several examples of Infix notations in Kotlin and also learned how to create an infix function.

Kotlin Infix Notation - Make function calls more intuitive
07
Mar
2021

Kotlin Infix Notation – Make function calls more intuitive

Kotlin supports method calls of a special kind, called infix calls.

You can mark any member function or extension function with the infix modifier to allow it to be called using infix notation. The only requirement is that the function should have only one required parameter.

Infix notations are used extensively in Kotlin. If you’ve been programming in Kotlin, chances are that you’ve already used infix notations.

Following are few common examples of infix notations in Kotlin –

1. Infix Notation Example – Creating a Map

val map = mapOf(1 to "one", 2 to "two", 3 to "three")

In the above example, the expressions 1 to "one"2 to "two" etc, are infix notations of the function calls 1.to("one") and 2.to("two") etc.

to() is an infix function that creates a Pair<A, B> from two values.

2. Infix Notation Example – Range Operators (until, downTo, step)

Kotlin provides various range operators that are usually called using infix notation –

for(i in 1 until 10) {	// Same as - for(i in 1.until(10))
    print("$i ")
}
for(i in 10 downTo 1) {	 // Same as - for(i in 10.downTo(1))
    print("$i ")
}
for(i in 1 until 10 step 2) { // Same as - for(i in 1.until(10).step(2))
    print("$i ")
}

3. Infix Notation Example – String.matches()

The String.matches() function in Kotlin which matches a String with a Regex is an infix function –

val regex = Regex("[tT]rue|[yY]es")
val str = "yes"

str.matches(regex)

// Infix notation of the above function call -
str matches regex

Creating an Infix Function

You can make a single argument member function or extension function, an infix function by marking it with the infix keyword.

Check out the following example where I have created an infix member function called add() for adding two Complex numbers –

data class ComplexNumber(val realPart: Double, val imaginaryPart: Double) {
	// Infix function for adding two complex numbers
    infix fun add(c: ComplexNumber): ComplexNumber {
        return ComplexNumber(realPart + c.realPart, imaginaryPart + c.imaginaryPart)
    }
}

You can now call the add() method using infix notation –

val c1 = ComplexNumber(3.0, 5.0)
val c2 = ComplexNumber(4.0, 7.0)

// Usual call
c1.add(c2) // produces - ComplexNumber(realPart=7.0, imaginaryPart=12.0)

// Infix call
c1 add c2  // produces - ComplexNumber(realPart=7.0, imaginaryPart=12.0)

Conclusion

That’s all folks. In this article, You learned what infix notation is and how it works. You saw several examples of Infix notations in Kotlin and also learned how to create an infix function.

Thanks for reading. Please ask any doubts in the comment section below.

Kotlin Functions, Default and Named Arguments, Varargs and Function Scopes
07
Mar
2021

Kotlin Functions, Default and Named Arguments, Varargs and Function Scopes

Functions are the basic building block of any program. In this article, you’ll learn how to declare and call functions in Kotlin. You’ll also learn about Function scopes, Default arguments, Named Arguments, and Varargs.

Defining and Calling Functions

You can declare a function in Kotlin using the fun keyword. Following is a simple function that calculates the average of two numbers –

fun avg(a: Double, b: Double): Double {
    return  (a + b)/2
}

Calling a function is simple. You just need to pass the required number of parameters in the function name like this –

avg(4.6, 9.0)  // 6.8

Following is the general syntax of declaring a function in Kotlin.

fun functionName(param1: Type1, param2: Type2,..., paramN: TypeN): Type {
	// Method Body
}

Every function declaration has a function name, a list of comma-separated parameters, an optional return type, and a method body. The function parameters must be explicitly typed.

Single Expression Functions

You can omit the return type and the curly braces if the function returns a single expression. The return type is inferred by the compiler from the expression –

fun avg(a: Double, b: Double) = (a + b)/2
avg(10.0, 20.0)  // 15.0

Note that, Unlike other statically typed languages like Scala, Kotlin does not infer return types for functions with block bodies. Therefore, Functions with block body must always specify return types explicitly.

Unit returning Functions

Functions which don’t return anything has a return type of Unit. The Unit type corresponds to void in Java.

fun printAverage(a: Double, b: Double): Unit {
    println("Avg of ($a, $b) = ${(a + b)/2}")
}

printAverage(10.0, 30.0)  // Avg of (10.0, 30.0) = 20.0

Note that, the Unit type declaration is completely optional. So you can also write the above function declaration like this –

fun printAverage(a: Double, b: Double) {
    println("Avg of ($a, $b) = ${(a + b)/2}")
}

Function Default Arguments

Kotlin supports default arguments in function declarations. You can specify a default value for a function parameter. The default value is used when the corresponding argument is omitted from the function call.

Consider the following function for example –

fun displayGreeting(message: String, name: String = "Guest") {
    println("Hello $name, $message")
}

If you call the above function with two arguments, it works just like any other function and uses the values passed in the arguments –

displayGreeting("Welcome to the CalliCoder Blog", "John") // Hello John, Welcome to the CalliCoder Blog

However, If you omit the argument that has a default value from the function call, then the default value is used in the function body –

displayGreeting("Welcome to the CalliCoder Blog") // Hello Guest, Welcome to the CalliCoder Blog

If the function declaration has a default parameter preceding a non-default parameter, then the default value cannot be used while calling the function with position-based arguments.

Consider the following function –

fun arithmeticSeriesSum(a: Int = 1, n: Int, d: Int = 1): Int {
    return n/2 * (2*a + (n-1)*d)
}

While calling the above function, you can not omit the argument a from the function call and selectively pass a value for the non-default parameter n –

arithmeticSeriesSum(10) // error: no value passed for parameter n

When you call a function with position-based arguments, the first argument corresponds to the first parameter, the second argument corresponds to the second parameter, and so on.

So for passing a value for the 2nd parameter, you need to specify a value for the first parameter as well –

arithmeticSeriesSum(1, 10)  // Result = 55

However, The above use-case of selectively passing a value for a parameter is solved by another feature of Kotlin called Named Arguments.

Function Named Arguments

Kotlin allows you to specify the names of arguments that you’re passing to the function. This makes the function calls more readable. It also allows you to pass the value of a parameter selectively if other parameters have default values.

Consider the following arithmeticSeriesSum() function that we defined in the previous section –

fun arithmeticSeriesSum(a: Int = 1, n: Int, d: Int = 1): Int {
    return n/2 * (2*a + (n-1)*d)
}

You can specify the names of arguments while calling the function like this –

arithmeticSeriesSum(n=10)  // Result = 55

The above function call will use the default values for parameters a and d.

Similarly, you can call the function with all the parameters like this –

arithmeticSeriesSum(a=3, n=10, d=2)  // Result = 120

You can also reorder the arguments if you’re specifying the names –

arithmeticSeriesSum(n=10, d=2, a=3)  // Result = 120

You can use a mix of named arguments and position-based arguments as long as all the position-based arguments are placed before the named arguments –

arithmeticSeriesSum(3, n=10)  // Result = 75

The following function call is not allowed since it contains position-based arguments after named arguments –

arithmeticSeriesSum(n=10, 2) // error: mixing named and positioned arguments is not allowed

Variable Number of Arguments (Varargs)

You can pass a variable number of arguments to a function by declaring the function with a vararg parameter.

Consider the following sumOfNumbers() function which accepts a vararg of numbers –

fun sumOfNumbers(vararg numbers: Double): Double {
    var sum: Double = 0.0
    for(number in numbers) {
        sum += number
    }
    return sum
}

You can call the above function with any number of arguments –

sumOfNumbers(1.5, 2.0)  // Result = 3.5

sumOfNumbers(1.5, 2.0, 3.5, 4.0, 5.8, 6.2)  // Result = 23.0

sumOfNumbers(1.5, 2.0, 3.5, 4.0, 5.8, 6.2, 8.1, 12.4, 16.5)  // Result = 60.0

In Kotlin, a vararg parameter of type T is internally represented as an array of type T (Array<T>) inside the function body.

A function may have only one vararg parameter. If there are other parameters following the vararg parameter, then the values for those parameters can be passed using the named argument syntax –

fun sumOfNumbers(vararg numbers: Double, initialSum: Double): Double {
    var sum = initialSum
    for(number in numbers) {
        sum += number
    }
    return sum
}
sumOfNumbers(1.5, 2.5, initialSum=100.0) // Result = 104.0

Spread Operator

Usually, we pass the arguments to a vararg function one-by-one. But if you already have an array and want to pass the elements of the array to the vararg function, then you can use the spread operator like this –

val a = doubleArrayOf(1.5, 2.6, 5.4)
sumOfNumbers(*a)  // Result = 9.5

Function Scope

Kotlin supports functional programming. Functions are first-class citizens in the language.

Unlike Java where every function needs to be encapsulated inside a class, Kotlin functions can be defined at the top level in a source file.

In addition to top-level functions, you also have the ability to define member functions, local functions, and extension functions.

1. Top Level Functions

Top level functions in Kotlin are defined in a source file outside of any class. They are also called package level functions because they are a member of the package in which they are defined.

The main() method itself is a top-level function in Kotlin since it is defined outside of any class.

Let’s now see an example of a top-level function. Check out the following findNthFibonacciNo() function which is defined inside a package named maths –

package maths

fun findNthFibonacciNo(n: Int): Int {
    var a = 0
    var b = 1
    var c: Int

    if(n == 0) {
        return a
    }

    for(i in 2..n) {
        c = a+b
        a = b
        b = c
    }
    return b
}

You can access the above function directly inside the maths package –

package maths

fun main(args: Array<String>) {
    println("10th fibonacci number is - ${findNthFibonacciNo(10)}")
}
//Outputs - 10th fibonacci number is - 55

However, If you want to call the findNthFibonacciNo() function from other packages, then you need to import it as in the following example –

package test
import maths.findNthFibonacciNo

fun main(args: Array<String>) {
    println("10th fibonacci number is - ${findNthFibonacciNo(10)}")
}

2. Member Functions

Member functions are functions which are defined inside a class or an object.

class User(val firstName: String, val lastName: String) {

	// Member function
    fun getFullName(): String {
        return firstName + " " + lastName
    }
}

Member functions are called on the objects of the class using the dot(.) notation –

val user = User("Bill", "Gates") // Create an object of the class
println("Display Name : ${user.getFullName()}") // Call the member function

3. Local/Nested Functions

Kotlin allows you to nest function definitions. These nested functions are called Local functions. Local functions bring more encapsulation and readability to your program –

fun findBodyMassIndex(weightInKg: Double, heightInCm: Double): Double {
    // Validate the arguments
    if(weightInKg <= 0) {
        throw IllegalArgumentException("Weight must be greater than zero")
    }
    if(heightInCm <= 0) {
        throw IllegalArgumentException("Height must be greater than zero")
    }

    fun calculateBMI(weightInKg: Double, heightInCm: Double): Double {
        val heightInMeter = heightInCm / 100
        return weightInKg / (heightInMeter * heightInMeter)
    }

    // Calculate BMI using the nested function
    return calculateBMI(weightInKg, heightInCm)
}

Local functions can access local variables of the outer function. So the above function is equivalent to the following –

fun findBodyMassIndex(weightInKg: Double, heightInCm: Double): Double {
    if(weightInKg <= 0) {
        throw IllegalArgumentException("Weight must be greater than zero")
    }
    if(heightInCm <= 0) {
        throw IllegalArgumentException("Height must be greater than zero")
    }

	// Nested function has access to the local variables of the outer function
    fun calculateBMI(): Double {
        val heightInMeter = heightInCm / 100
        return weightInKg / (heightInMeter * heightInMeter)
    }

    return calculateBMI()
}

Conclusion

Congratulations folks! In this article, you learned how to define and call functions in Kotlin, how to use default and named arguments, how to define and call functions with a variable number of arguments, and how to define top-level functions, member functions and local/nested functions

In future articles, I’ll write about extension functions, higher order functions and lambdas. So Stay tuned!

As always, Thank you for reading. Happy Kotlin Koding 🙂

Nullable Types and Null Safety in Kotlin
07
Mar
2021

Nullable Types and Null Safety in Kotlin

if you have been programming in Java or any other language that has the concept of null reference then you must have heard about or experienced NullPointerException in your programs.

NullPointerExceptions are Runtime Exceptions which are thrown by the program at runtime causing application failure and system crashes.

Wouldn’t it be nice if we could detect possible NullPointerException exception errors at compile time itself and guard against them?

Well, Enter Kotlin!

Nullability and Nullable Types in Kotlin

Kotlin supports nullability as part of its type System. That means You have the ability to declare whether a variable can hold a null value or not.

By supporting nullability in the type system, the compiler can detect possible NullPointerException errors at compile time and reduce the possibility of having them thrown at runtime.

Let’s understand how it works!

All variables in Kotlin are non-nullable by default. So If you try to assign a null value to a regular variable, the compiler will throw an error –

var greeting: String = "Hello, World"
greeting = null // Compilation Error

To allow null values, you have to declare a variable as nullable by appending a question mark in its type declaration –

var nullableGreeting: String? = "Hello, World"
nullableGreeting = null // Works

We know that NullPointerException occurs when we try to call a method or access a property on a variable which is null. Kotlin disallows method calls and property access on nullable variables and thereby prevents many possible NullPointerExceptions.

For example, The following method access works because Kotlin knows that the variable greeting can never be null –

val len = greeting.length 
val upper = greeting.toUpperCase() 

But the same method call won’t work with nullableGreeting variable –

val len = nullableGreeting.length // Compilation Error
val upper = nullableGreeting.toUpperCase()  // Compilation Error

Since Kotlin knows beforehand which variable can be null and which cannot, It can detect and disallow calls which could result in NullPointerException at compile-time itself.

Working with Nullable Types

All right, It’s nice that Kotlin disallows method calls and property access on nullable variables to guard against NullPointerException errors. But we still need to do that right?

Well, There are several ways of safely doing that in Kotlin.

1. Adding a null Check

The most trivial way to work with nullable variables is to perform a null check before accessing a property or calling a method on them –

val nullableName: String? = "John"

if(nullableName != null) {
    println("Hello, ${nullableName.toUpperCase()}.")
    println("Your name is ${nullableName.length} characters long.")
} else {
    println("Hello, Guest")
}

Once you perform a null comparison, the compiler remembers that and allows calls to toUpperCase() and length inside the if branch.

2. Safe call operator: ?.

Null Comparisons are simple but too verbose. Kotlin provides a Safe call operator, ?. that reduces this verbosity. It allows you to combine a null-check and a method call in a single expression.

For example, The following expression –

nullableName?.toUpperCase()

is same as –

if(nullableName != null) 
    nullableName.toUpperCase()
else
    null    

Wow! That saves a lot of keystrokes, right? 🙂

So if you were to print the name in uppercase and its length safely, you could do the following –

val nullableName: String? = null

println(nullableName?.toUpperCase())
println(nullableName?.length)
// Prints 
null
null

That printed null since the variable nullableName is null, otherwise, it would have printed the name in uppercase and its length.

But what if you don’t want to print anything if the variable is null?

Well, To perform an operation only if the variable is not null, you can use the safe call operator with let –

val nullableName: String? = null

nullableName?.let { println(it.toUpperCase()) }
nullableName?.let { println(it.length) }

// Prints nothing

The lambda expression inside let is executed only if the variable nullableName is not null.

That’s great but that’s not all. Safe call operator is even more powerful than you think. For example, You can chain multiple safe calls like this –

val currentCity: String? = user?.address?.city

The variable currentCity will be null if any of useraddress or city is null. (Imagine doing that using null-checks.)

3. Elvis operator: ?:

The Elvis operator is used to provide a default value when the original variable is null –

val name = nullableName ?: "Guest"

The above expression is same as –

val name = if(nullableName != null) nullableName else "Guest"

In other words, The Elvis operator takes two values and returns the first value if it is not null, otherwise, it returns the second value.

The Elvis operator is often used with Safe call operator to provide a default value other than null when the variable on which a method or property is called is null –

val len = nullableName?.length ?: -1

You can have more complex expressions on the left side of Elvis operator –

val currentCity = user?.address?.city ?: "Unknown"

Moreover, You can use throw and return expressions on the right side of Elvis operator. This is very useful while checking preconditions in a function. So instead of providing a default value in the right side of Elvis operator, you can throw an exception like this –

val name = nullableName ?: throw IllegalArgumentException("Name can not be null")

4. Not null assertion : !! Operator

The !! operator converts a nullable type to a non-null type, and throws a NullPointerException if the nullable type holds a null value.

So It’s a way of asking for NullPointerException explicitly. Please don’t use this operator.

val nullableName: String? = null
nullableName!!.toUpperCase() // Results in NullPointerException

Null Safety and Java Interoperability

Kotlin is fully interoperable with Java but Java doesn’t support nullability in its type system. So what happens when you call Java code from Kotlin?

Well, Java types are treated specially in Kotlin. They are called Platform types. Since Kotlin doesn’t have any information about the nullability of a type declared in Java, It relaxes compile-time null checks for these types.

So you don’t get any null safety guarantee for types declared in Java, and you have full responsibility for operations you perform on these types. The compiler will allow all operations. If you know that the Java variable can be null, you should compare it with null before use, otherwise, just like Java, you’ll get a NullPointerException at runtime if the value is null.

Consider the following User class declared in Java –

public class User {
    private final String name;

    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

Since Kotlin doesn’t know about the nullability of the member variable name, It allows all operations on this variable. You can treat it as nullable or non-nullable, but the compiler won’t enforce anything.

In the following example, We simply treat the variable name as non-nullable and call methods and properties on it –

val javaUser = User(null)

println(javaUser.name.toUpperCase()) // Allowed (Throws NullPointerException)
println(javaUser.name.length) // Allowed (Throws NullPointerException)

The other option is to treat the member variable name as nullable and use the safe operator for calling methods or accessing properties –

val javaUser = User(null)

println(javaUser.name?.toUpperCase()) // Allowed (Prints null)
println(javaUser.name?.length) // Allowed (Prints null)

Nullability Annotations

Although Java doesn’t support nullability in its type system, You can use annotations like @Nullable and @NotNull provided by external packages like javax.validation.constraintsorg.jetbrains.annotations etc to mark a variable as Nullable or Not-null.

Java compiler doesn’t use these annotations, but these annotations are used by IDEs, ORM libraries and other external tools to provide assistance while working with null values.

Kotlin also respects these annotations when they are present in Java code. Java types which have these nullability annotations are represented as actual nullable or non-null Kotlin types instead of platform types.

Nullability and Collections

Kotlin’s collection API is built on top of Java’s collection API but it fully supports nullability on Collections.

Just as regular variables are non-null by default, a normal collection also can’t hold null values –

val regularList: List<Int> = listOf(1, 2, null, 3) // Compiler Error

1. Collection of Nullable Types

Here is how you can declare a Collection of Nullable Types in Kotlin –

val listOfNullableTypes: List<Int?> = listOf(1, 2, null, 3) // Works

To filter non-null values from a list of nullable types, you can use the filterNotNull() function –

val notNullList: List<Int> = listOfNullableTypes.filterNotNull()

2. Nullable Collection

Note that there is a difference between a collection of nullable types and a nullable collection.

A collection of nullable types can hold null values but the collection itself cannot be null –

var listOfNullableTypes: List<Int?> = listOf(1, 2, null, 3) // Works
listOfNullableTypes = null // Compilation Error

You can declare a nullable collection like this –

var nullableList: List<Int>? = listOf(1, 2, 3)
nullableList = null // Works

3. Nullable Collection of Nullable Types

Finally, you can declare a nullable collection of nullable types like this –

var nullableListOfNullableTypes: List<Int?>? = listOf(1, 2, null, 3) // Works
nullableListOfNullableTypes = null // Works

Conclusion

That’s all in this article folks. I hope you understood how kotlin helps you avoid NullPointerException errors with its nullable type concept.

Thanks for reading. See you in the next post.

Kotlin Control Flow: if and when expressions, for and while loops
07
Mar
2021

Kotlin Control Flow: if and when expressions, for and while loops

n this article, You’ll learn how to use Kotlin’s control flow expressions and statements which includes conditional expressions like ifif-elsewhen and looping statements like forwhile, and do-while.

If Statement

The If statement allows you to specify a section of code that is executed only if a given condition is true-

var n = 34
if(n % 2 == 0) {
	println("$n is even")
}

// Displays - "34 is even"

The curly braces are optional if the body of if statement contains a single line –

if(n % 2 == 0) println("$n is even")

If-Else Statement

The if-else statement executes one section of code if the condition is true and the other if the condition is false –

var a = 32
var b = 55

if(a > b) {
    println("max($a, $b) = $a")
} else {
    println("max($a, $b) = $b")
}

// Displays - "max(32, 55) = 55"

Using If as an Expression

In Kotlin, You can use if as an expression instead of a statement. For example, you can assign the result of an if-else expression to a variable.

Let’s rewrite the if-else example of finding the maximum of two numbers that we saw in the previous section as an expression –

var a = 32
var b = 55

var max = if(a > b) a else b
println("max($a, $b) = $max")

// Displays - "max(32, 55) = 55"

Note that when you’re using if as an expression, it is required to have an else branch, otherwise, the compiler will throw an error.

The if-else branches can also have block bodies. In case of block bodies, the last expression is the value of the block –

var a = 32
var b = 55

var max = if(a > b) {
    println("$a is greater than $b")
    a
} else {
    println("$a is less than or equal to $b")
    b
}
println("max($a, $b) = $max")
# Output
32 is less than or equal to 55
max(32, 55) = 55

Unlike Java, Kotlin doesn’t have a ternary operator because we can easily achieve what ternary operator does, using an if-else expression.

If-Else-If Chain

You can chain multiple if-else-if blocks like this –

var age = 17
if(age < 12) {
    println("Child")
} else if (age in 12..17) {
    println("Teen")
} else if (age in 18..21) {
    println("Young Adult")
} else if (age in 22..30) {
    println("Adult")
} else if (age in 30..50) {
    println("Middle Aged")
} else {
    println("Old")
}

// Displays - "Teen"

In the next section, we’ll learn how to represent if-else-if chain using a when expression to make it more concise.

When Expression

Kotlin’s when expression is the replacement of switch statement from other languages like C, C++, and Java. It is concise and more powerful than switch statements.

Here is how a when expression looks like –

var dayOfWeek = 4
when(dayOfWeek) {
    1 -> println("Monday")
    2 -> println("Tuesday")
    3 -> println("Wednesday")
    4 -> println("Thursday")
    5 -> println("Friday")
    6 -> println("Saturday")
    7 -> println("Sunday")
    else -> println("Invalid Day")
}
// Displays - "Thursday"

when expression matches the supplied argument with all the branches one by one until a match is found. Once a match is found, it executes the matched branch. If none of the branches match, the else branch is executed.

In the above example, all the branches contain a single statement. But they can also contain multiple statements enclosed in a block –

var dayOfWeek = 1
when(dayOfWeek) {
    1 -> {
        // Block
        println("Monday")
        println("First day of the week")
    }
    7 -> println("Sunday")
    else -> println("Other days")
}

Using when as an expression

Just like ifwhen can be used as an expression and we can assign its result to a variable like so –

var dayOfWeek = 4

var dayOfWeekInString = when(dayOfWeek) {
    1 -> "Monday"
    2 -> "Tuesday"
    3 -> "Wednesday"
    4 -> "Thursday"
    5 -> "Friday"
    6 -> "Saturday"
    7 -> "Sunday"
    else -> "Invalid Day"
}

println("Today is $dayOfWeekInString")	// Today is Thursday

Combining multiple when branches into one using comma

You can combine multiple branches into one using comma. This is helpful when you need to run a common logic for multiple cases –

var dayOfWeek = 6
when(dayOfWeek) {
    1, 2, 3, 4, 5 -> println("Weekday")
    6, 7 -> println("Weekend")
    else -> println("Invalid Day")
}
// Displays - Weekend

Checking whether a given value is in a range or not using in operator

range is created using the .. operator. For example, you can create a range from 1 to 10 using 1..10. You’ll learn more about range in a future article.

The in operator allows you to check if a value belongs to a range/collection –

var dayOfMonth = 5
when(dayOfMonth) {
    in 1..7 -> println("We're in the first Week of the Month")
    !in 15..21 -> println("We're not in the third week of the Month")
    else -> println("none of the above")
}
// Displays - We're in the first Week of the Month

Checking whether a given variable is of certain type or not using is operator

var x : Any = 6.86
when(x) {
    is Int -> println("$x is an Int")
    is String -> println("$x is a String")
    !is Double -> println("$x is not Double")
    else -> println("none of the above")
}
// Displays - none of the above

Using when as a replacement for an if-else-if chain

var number = 20
when {
    number < 0 -> println("$number is less than zero")
    number % 2 == 0 -> println("$number is even")
    number > 100 -> println("$number is greater than 100")
    else -> println("None of the above")
}
// Displays - 20 is even

While Loop

While loop executes a block of code repeatedly as long as a given condition is true –

while(condition) {
    // code to be executed
}

Here is an example –

var x = 1
while(x <= 5) {
    println("$x ")
    x++
}

// Displays - 1 2 3 4 5

In the above example, we increment the value of x by 1 in each iteration. When x reaches 6, the condition evaluates to false and the loop terminates.

do-while loop

The do-while loop is similar to while loop except that it tests the condition at the end of the loop.

var x = 1
do {
    print("$x ")
    x++
} while(x <= 5)

// Displays - 1 2 3 4 5

Since do-while loop tests the condition at the end of the loop. It is executed at least once –

var x = 6
do {
    print("$x ")
    x++
} while(x <= 5)

// Displays - 6

For Loop

A for-loop is used to iterate through ranges, arrays, collections, or anything that provides an iterator (You’ll learn about iterator in a future article).

Iterating through a range

for(value in 1..10) {
    print("$value ")
}
// Displays - 1 2 3 4 5 6 7 8 9 10

Iterating through an array

var primeNumbers = intArrayOf(2, 3, 5, 7, 11)

for(number in primeNumbers) {
    print("$number ")
}
// Displays - 2, 3, 5, 7, 11

Iterating through an array using its indices

Every array in Kotlin has a property called indices which returns a range of valid indices of that array.

You can iterate over the indices of the array and retrieve each array element using its index like so –

var primeNumbers = intArrayOf(2, 3, 5, 7, 11)

for(index in primeNumbers.indices) {
    println("PrimeNumber(${index+1}): ${primeNumbers[index]}")
}
# Output
PrimeNumber(1): 2
PrimeNumber(2): 3
PrimeNumber(3): 5
PrimeNumber(4): 7
PrimeNumber(5): 11

Iterating through an array using withIndex()

You can use the withIndex() function on arrays to obtain an iterable of IndexedValue type. This allows you to access both the index and the corresponding array element in each iteration –

var primeNumbers = intArrayOf(2, 3, 5, 7, 11)

for((index, number) in primeNumbers.withIndex()) {
    println("PrimeNumber(${index+1}): $number")
}

The output of this snippet is same as the previous snippet.

Break and Continue

Break out of a loop using the break keyword

for (num in 1..100) {
    if (num%3 == 0 && num%5 == 0) {
        println("First positive no divisible by both 3 and 5: ${num}")
        break
    }
}
# Output
First positive no divisible by both 3 and 5: 15

Skip to the next iteration of a loop using the continue keyword

for (num in 1..10) {
    if (num%2 == 0) {
        continue;
    }
    print("${num} ")
}
# Output
1 3 5 7 9 

Conclusion

That’s all folks! In this article, you learned how to use Kotlin’s conditional expressions like ifif-elsewhen, and looping statements like forwhile and do-while. You can find more articles from the sidebar menu.