Category: Kotlin

Kotlin is a cross-platform, statically typed, general-purpose programming language with type inference. Kotlin is designed to interoperate fully with Java, and the JVM version of Kotlin’s standard library depends on the Java Class Library, but type inference allows its syntax to be more concise.

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 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 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 🙂

A Guide on How to Write a Clean Code
18
Mar
2021

A Guide on How to Write a Clean Code

In this article, I’m going to talk about writing clean code in general and then end up with some examples. As an Android developer having a clean code has always been challenging for me which needs lots of effort and that’s just coding and coding.

Rules We Should Follow

  1. Ignore duplication (imply DRY principle-Don’t Repeat Yourself)
  2. Minimize entities, classes, and functions (avoid repetition)
  3. It should be readable and simple
  4. Be testable as it makes your code flexible and maintainable
  5. Follow SOLIDprinciples

S = Single-responsibility principle: A class should only have one purpose.

O = Open-closed principleA class should be open for extension, but closed for modification.

L = Liskov substitution principleAbstraction should be able to provide all needs of child class.

I = Interface segregation principleSmall interface is better than big one.

D = Dependency Inversion Principle: A class should depend on abstraction, not implementation.

6. Be careful with dependencies

As many as possible try to have one-directional dependency. When dependency goes in multiple directions, things get much more complicated which makes hard to update and change.

7. Don’t hardcode

Define constant or use variables instead of hardcoding the values which will not only help readability but also make it easy to change if it is being used at multiple places.

Now let’s start writing Clean Code 🚀 …

Valid Names

Naming is one of the hardest parts of programming and may take time to choose, but it is a strong way to convey your code’s intent to other developers who read them in the future which must show the purpose of that class, function, or variable and even tell what is going to do. Don’t append prefixes or type information. I always have a checklist in my mind as listed below which assures me that a name has been chosen well:

  • Are the variables named according to the convention (camelCase, PascalCase, etc)?.
  • Does the name have an appropriate length to ensure that no one will be confused by it?.
  • Are the name of variables clear about what they hold?.
  • Are the names meaningful, searchable, and easy to pronounce?.

Class names should not only be names (not verbs) but also should have the PascalCase convention. On the other hand, method names should be verbs or phrase verbs and follow the camelCase convention. The same rules apply to variable names.

Functions and Methods

Following S from SOLID principles, let functions and methods perform only one task which should be small. If the function arguments are too many, you should review your code and maybe pack them into an object or divide that task between some other functions. Prefer possible exceptions to return error codes and extract error handling try catch into their own function.

Comments

We should use comments only when it is necessary, not to explain bad code. Writing lengthy comments will not help us in changing our code into a clean one. If the code is bad, we should solve it by improving the code, not by adding instructions on how to use it, but it doesn’t mean that you shouldn’t use comments at all, sometimes it is important such as dealing with third party APIs where you need to explain some behavior.

Code Appearance

It might look unimportant at first glance, but it is of high importance to write your code with a well-organized format which makes it readable such as:

  • Do not write everything in a single line. Give proper whitespace, indentation, or line breaks in your code.

Indentation styles assist in identifying control flow and blocks of code. In some programming languages, indentation is used to delimit logical blocks of code; correct indentation in these cases is more than a matter of style. In other languages, indentation and white space do not affect function, although logical and consistent indentation makes code more readable. (Inspired by WIKIPEDIA)

  • Instance variables should be declared at the top of the class.
  • If functions are calling each other (dependent functions) they should be close while putting the caller at first.
  • It is often helpful to align similar elements vertically, to make typo-generated bugs more obvious.

Tests

The importance of Test code is equal to producing the code and of course, while writing tests you might find some bugs. However, it can sometimes be quite difficult to write a good test for a particular piece of code which is a result of a badly designed and untestable code. Testing is almost like coding for the second time so follow all the previous rules for writing a good test as well which should be as per below :

  • One assert and single concept per test
  • Easy to write and readable (simplicity)
  • Fast
  • Independent

To conclude, I hope this article could be helpful to write a better code although it is important to keep practicing and learning in this field 😃 …

Kotlin Variables and Data Types
07
Mar
2021

Kotlin Variables and Data Types

In this article, You’ll learn how to declare variables in Kotlin, how Kotlin infers the type of variables, and what are the basic data types supported by Kotlin for creating variables.

You’ll also learn how to work with various data types and how to convert one type to another.

Variables

A variable refers to a memory location that stores some data. It has a name and an associated type. The type of a variable defines the range of values that the variable can hold, and the operations that can be done on those values.

You can declare a variable in Kotlin using var and val keywords.

A variable declared using val keyword is immutable (read-only). It cannot be reassigned after it is initialized –

val name = "Bill Gates"
name = "Satoshi Nakamoto"	// Error: Val cannot be reassigned 

For defining a mutable variable, i.e. a variable whose value can be changed, use the var keyword –

var country = "USA"
country = "India"    // Works

Type inference

Did you notice one thing about the variable declarations in the previous section? We didn’t specify the type of variables.

Although Kotlin is a statically typed language, It doesn’t require you to explicitly specify the type of every variable you declare. It can infer the type of a variable from the initializer expression –

val greeting = "Hello, World"  // type inferred as `String`
val year = 2018                // type inferred as `Int`

If you want to explicitly specify the type of a variable, you can do that like this –

// Explicitly defining the type of variables
val greeting: String = "Hello, World" 
val year: Int = 2018

Note that the type declaration becomes mandatory if you’re not initializing the variable at the time of declaration –

var language   // Error: The variable must either have a Type annotation or be initialized
language =  "French"

The above variable declaration fails because Kotlin has no way to infer the type of the variable without an initializer expression. In this case, you must explicitly specify the type of the variable –

var language: String   // Works
language = "French"

Data Types

Data Types are used to categorize a set of related values and define the operations that can be done on them.

Just like other languages, Kotlin has predefined types like IntDouble BooleanChar etc.

In Kotlin, everything (even the basic types like Int and Boolean) is an object. More specifically, everything behaves like an Object.

Kotlin may represent some of the basic types like numbers, characters and booleans as primitive values at runtime to improve performance, but for the end users, all of them are objects.

This is contrary to languages like Java that has separate primitive types like intdoubleetc, and their corresponding wrapper types like IntegerDouble etc.

Let’s now look at all the basic data types used in Kotlin one by one –

Numbers

Numeric types in Kotlin are similar to Java. They can be categorized into integer and floating point types.

Integers

  • Byte – 8 bit
  • Short– 16 bit
  • Int – 32 bit
  • Long – 64 bit

Floating Point Numbers

  • Float – 32 bit single-precision floating point value.
  • Double – 64 bit double-precision floating point value.

Following are few examples of numeric types –

// Kotlin Numeric Types Examples
val myByte: Byte = 10
val myShort: Short = 125

val myInt = 1000
val myLong = 1000L	// The suffix 'L' is used to specify a long value

val myFloat = 126.78f   // The suffix 'f' or 'F' represents a Float 
val myDouble = 325.49

You can also use underscore in numeric values to make them more readable –

val hundredThousand = 100_000
val oneMillion = 1_000_000

You can declare hexadecimal and binary values like this –

val myHexa = 0x0A0F  // Hexadecimal values are prefixed with '0x' or '0X'
val myBinary = 0b1010  // Binary values are prefixed with '0b' or '0B'

Note that Kotlin doesn’t have any representation for octal values.

Booleans

The type Boolean is used to represent logical values. It can have two possible values true and false.

val myBoolean = true
val anotherBoolean = false

Characters

Characters are represented using the type Char. Unlike Java, Char types cannot be treated as numbers. They are declared using single quotes like this –

val letterChar = 'A'
val digitChar = '9'

Just like other languages, special characters in Kotlin are escaped using a backslash. Some examples of escaped characters are – \n (newline), \t (tab), \r (carriage return), \b (backspace) etc.

Strings

Strings are represented using the String class. They are immutable, that means you cannot modify a String by changing some of its elements.

You can declare a String like this –

var myStr = "Hello, Kotlin"

You can access the character at a particular index in a String using str[index]. The index starts from zero –

var name = "John"
var firstCharInName = name[0]  // 'J'
var lastCharInName = name[name.length - 1]  // 'n'

The length property is used to get the length of a String.

Escaped String and Raw String

Strings declared in double quotes can have escaped characters like ‘\n’ (new line), ‘\t’ (tab) etc –

var myEscapedString = "Hello Reader,\nWelcome to my Blog"

In Kotlin, you also have an option to declare raw strings. These Strings have no escaping and can span multiple lines –

var myMultilineRawString = """
    The Quick Brown Fox
    Jumped Over a Lazy Dog.
"""

Arrays

Arrays in Kotlin are represented using the Array class. You can create an array in Kotlin either using the library function arrayOf() or using the Array() constructor.

Creating Arrays using the arrayOf library function

You can pass a bunch of values to the arrayOf function to create an array like this –

var numbers = arrayOf(1, 2, 3, 4, 5)
var animals = arrayOf("Cat", "Dog", "Lion", "Tiger")

Note that you can also pass values of mixed types to the arrayOf() function, and it will still work (but don’t do that) –

var mixedArray = arrayOf(1, true, 3, "Hello", 'A')	// Works and creates an array of Objects

You can also enforce a particular type while creating the array like this –

var numArray = arrayOf<Int>(1, 2, 3, 4, "Hello")  // Compiler Error

Accessing the elements of an array by their index

You can access the element at a particular index in an array using array[index]. The index starts from zero –

val myDoubleArray = arrayOf(4.0, 6.9, 1.7, 12.3, 5.4)
val firstElement = myDoubleArray[0]
val lastElement = myDoubleArray[myDoubleArray.size - 1]

Every array has a size property that you can use to get the size of the array.

You can also modify the array element at an index like this –

val a = arrayOf(4, 5, 7)  // [4, 5, 7]
a[1] = 10		          // [4, 10, 7]	

Primitive Arrays

As we learned earlier, everything in Kotlin is an object. But to improve performance it represents some of the basic types like numbers, characters and booleans as primitive types at runtime.

The arrayOf() function creates arrays of boxed/wrapper types. That is, arrayOf(1, 2, 3) corresponds to Java’s Integer[] array.

But, Kotlin provides a way to create arrays of primitive types as well. It contains specialized classes for representing array of primitive types. Those classes are – IntArrayDoubleArrayCharArray etc. You can create an array of primitive types using the corresponding library functions – intArrayOf()doubleArrayOf()charArrayOf() etc. –

val myCharArray = charArrayOf('K', 'O', 'T')  // CharArray (corresponds to Java 'char[]')
val myIntArray = intArrayOf(1, 3, 5, 7)		// IntArray (corresponds to Java 'int[]')

Creating Arrays using the Array() constructor

The Array() constructor takes two arguments –

  1. the size of the array, and
  2. a function that takes the array index as an argument and returns the element to be inserted at that index.
var mySquareArray = Array(5, {i -> i * i})	// [0, 1, 4, 9, 16]

The second argument to the Array() constructor is a lambda expression. Lambda expressions are anonymous functions that are declared and passed around as expressions. We’ll learn more about lambda expressions in a future tutorial.

The above lambda expression takes the index of an array element and returns the value that should be inserted at that index, which is the square of the index in this case.

Type Conversions

Unlike Java, Kotlin doesn’t support implicit conversion from smaller types to larger types. For example, Int cannot be assigned to Long or Double.

var myInt = 100
var myLong: Long = myInt // Compiler Error

However, Every number type contains helper functions that can be used to explicitly convert one type to another.

Following helper functions are supported for type conversion between numeric types –

  • toByte()
  • toShort()
  • toInt()
  • toLong()
  • toFLoat()
  • toDouble()
  • toChar()

Examples of explicit type conversions

Here is how you can convert an Int to Long –

val myInt = 100
val myLong = myInt.toLong()   // Explicitly converting 'Int' to 'Long'

You can also convert larger types to smaller types –

val doubleValue = 176.80
val intValue = doubleValue.toInt()  // 176

Every type in Kotlin, not just numeric type, supports a helper function called toString() to convert it to String.

val myInt = 1000
myInt.toString()  // "1000"

You can also convert a String to a numeric type like so –

val str = "1000"
val intValue = str.toInt()

If the String-to-Number conversion is not possible then a NumberFormatException is thrown –

val str = "1000ABC"
str.toInt()   // Throws java.lang.NumberFormatException

Conclusion

That’s all folks! In this article, You learned how to declare mutable and immutable variables. How type inference works in Kotlin. What are the basic data types supported in Kotlin. How to work with data types like IntLong DoubleCharBooleanString and Array, and how to convert one type to another.

Writing your first Kotlin program
07
Mar
2021

Writing your first Kotlin program

The first program that we typically write in any programming language is the “Hello, World” program. Let’s write the “Hello, World” program in Kotlin and understand its internals.

The “Hello, World!” program in Kotlin

Open your favorite editor or an IDE and create a file named hello.kt with the following code –

// Kotlin Hello World Program
fun main(args: Array<String>) {
    println("Hello, World!")
}

You can compile and run the program using Kotlin’s compiler like so –

$ kotlinc hello.kt
$ kotlin HelloKt
Hello, World!

You can also run the program in an IDE like Eclipse or IntelliJ. Learn how to Setup and run Kotlin programs in your system.

Internals of the “Hello, World!” program

  • Line 1. The first line is a comment. Comments are ignored by the compiler. They are used for your own sake (and for others who read your code).Kotlin supports two different styles of comments similar to other languages like C, C++, and Java –
    1. Single Line Comment:// This is a Single line comment
    2. Multi-line Comment:/* This is an example of a multi-line comment. It can span over multiple lines. */
  • Line 2. The second line defines a function called main.fun main(args: Array<String>) { // ... } Functions are the building blocks of a Kotlin program. All functions in Kotlin start with the keyword fun followed by a name of the function (main in this case), a list of zero or more comma-separated parameters, an optional return type, and a body.The main() function takes one argument – an Array of strings, and returns Unit. The Unit type corresponds to void in Java. It is used to indicate that the function doesn’t return any meaningful value.The Unit type is optional. i.e. you don’t need to declare it explicitly in the function declaration –fun main(args: Array<String>): Unit { // `Unit` is optional } If you don’t specify a return type, Unit is assumed.The main() function is not just any ordinary function. It is the entry point of your program. It is the first thing that gets called when you run a Kotlin program.
  • Line 3. The third line is a statement. It prints a String “Hello, World!” to standard output.println("Hello, World!") // No Semicolons needed :) Note that we can directly use println() to print to standard output, unlike Java where we need to use System.out.println().Kotlin provides several wrappers over standard Java library functions, println is one of them.Also, Semicolons are optional in Kotlin, just like many other modern languages.

Conclusion

Congratulations! You just wrote your first program in Kotlin. You learned about the main method, single-line and multi-line comments, and the println library function.

In the next article, you’ll learn how to create variables in Kotlin and also look at various data types that are supported for creating variables.

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 Overview, Installation, and Setup
07
Mar
2021

Kotlin Overview, Installation, and Setup

Kotlin is a programming language developed by JetBrains, the same company that has built world-class IDEs like IntelliJ IDEA, PhpStorm, PyCharm, ReSharper etc.

It runs on the Java Virtual Machine (JVM), and can also be compiled to JavaScript and Machine Code.

In this tutorial, I’ll give you a brief overview of Kotlin and its features. I’ll also help you set up Kotlin in your system and prepare you for future tutorials.

Why Kotlin?

In today’s world where we have a dozen programming language for every possible task, following are few reasons to choose Kotlin as the primary language for your next exciting project –

1. Statically Typed

Kotlin is a Statically typed programming language. This means that the type of every variable and expression is known at compile time.

The advantage with static typing is that the compiler can validate the methods calls and property access on the objects at compile time itself and prevent lots of trivial bugs that would otherwise crop up at runtime.

Although Kotlin is a statically typed language, it doesn’t require you to explicitly specify the type of every variable you declare. Most of the time, Kotlin can infer the type of a variable from the initializer expression or the surrounding context. This is called Type Inference. You’ll learn more about Type inference in the Variables and Data Types tutorial.

2. Concise

Kotlin is concise. It drastically reduces the amount of boilerplate code that you have been writing all the time in other OOP languages like Java.

It provides rich idioms for performing common tasks. For example, You can create a POJO class with getters, setters, equals()hashCode() and toString() methods in a single line –

data class User(val name: String, val email: String, val country: String)

3. Safe

Kotlin is safe. It avoids the most dreaded and annoying NullPointerExceptions by supporting nullability as part of its type system.

It works like this – Every variable in Kotlin is non-null by default:

String str = "Hello, World"    // Non-null type (can't hold null value)
str = null // Compiler Error

To allow a variable to hold null value, you need to explicitly declare it as nullable:

String nullableStr? = null   // Nullable type (can be null)

Since Kotlin knows which variables are nullable and which are not, It can detect and disallow unsafe calls at compile time itself that would otherwise result in a NullPointerException at runtime –

println(nullableStr.length()) // Compiler Error

Kotlin doesn’t allow the method call length() on the nullableStr variable because the call is not safe and may lead to NullPointerException.

However, if you add a null check then the method call is allowed –

if(nullablStr != null) {
    println(nullableStr.length())
}

Notice how Kotlin is enforcing developers to write safe code by distinguishing between nullable and non-null types.

4. Explicit

Kotlin is Explicit. It will do/allow things only if you tell it to do so. Explicitness is considered a good thing. Being explicit means being specific about your design choices and not hiding anything from the readers or consumers of your code.

Following are few examples of Explicitness in Kotlin –

  • Kotlin doesn’t allow implicit type conversions, for example, int to long, or float to double. It provides methods like toLong() and toDouble() to do so explicitly.
  • All the classes in Kotlin are final (non-inheritable) by default. You need to explicitly mark a class as open to allow other classes to inherit from it. Similarly, All the properties and member functions of a class are final by default. You need to explicitly mark a function or property as open to allow child classes to override it.
  • If you’re overriding a parent class function or property, then you need to explicitly annotate it with the override modifier.

5. Easy to learn.

Kotlin has a very low learning curve. The basic syntax looks a lot like Java. If you have a little experience in Java or any other OOP language then you’ll be able to pick up Kotlin in a matter of hours.

6. Functional and Object Oriented Capabilities

Kotlin has both functional and object-oriented capabilities. It has a rich set of features to support functional programming which includes functional types, lambda expressions, data classes and much more.

7. Completely interoperable with Java

Kotlin is 100% interoperable with Java. You can easily access Java code from Kotlin and vice versa. You can use Kotlin and Java in the same project without any problem. This enables easy adoption of Kotlin into your existing Java projects.

Kotlin Java Interoperability

8. Excellent Tooling

Kotlin has excellent tooling support. You can choose any Java IDE – IntelliJ IDEA, Eclipse, Android Studio. All of them support Kotlin.

Moreover, you can also download Kotlin’s standalone compiler and run Kotlin code from the command line.

Kotlin Tool Friendly

9. Build Applications for Server Side, Android, Browser, and Desktop

You can use Koltin to build applications for a wide range of platforms including Server side, Android, Browser, and Desktop.

  • Android has official support for Kotlin.
  • On the server side, you can use Kotlin with the Spring framework which has added full support for Kotlin in Spring version 5.
  • Kotlin can be compiled to JavaScript and Machine code as well.

10. Free and Open Source

Kotlin programming language, including the compiler, libraries and all the tooling is completely free and open source. It is available under Apache 2 license and the complete project is hosted on Github – https://github.com/JetBrains/kotlin

Setup Kotlin

You can set up and run Kotlin programs in several ways. You can either install kotlin’s compiler and run Kotlin programs from the command line or install and setup Kotlin in an IDE like IntelliJ or Eclipse –

  • Install Kotlin’s Standalone Compiler
  • Setup Kotlin in IntelliJ IDEA
  • Setup Kotlin in Eclipse

Installing the Standalone Compiler

Follow the steps below to install Kotlin’s compiler –

  1. Go to Kotlin releases page on Github
  2. Download Kotlin’s compiler in the form of a zip file from the Assets section on the Github releases page. The latest version of Kotlin compiler at the time of writing this page is 1.2.10
  3. Unzip the downloaded kotlin-compiler-x.x.x.zip file and store the unzipped folder in a location where you have write access.
  4. Add path-to-unzipped-folder/bin to your PATH variable.
  5. Verify the installation by typing kotlinc in the command line –
$ kotlinc
Welcome to Kotlin version 1.2.10 (JRE 1.8.0_112-b16)
Type :help for help, :quit for quit
>>>

Run your first Kotlin program from the command line

Open your favorite editor and create a new file called hello.kt with the following contents –

fun main(args: Array<String>) {
    println("Hello, World!")
}

Save the file and type the following commands to compile and run the program

$ kotlinc hello.kt
$ kotlin HelloKt
Hello, World

Setting up Kotlin in IntelliJ IDEA

Install the latest version of IntelliJ IDEA. Kotlin comes bundled with the recent versions of IntelliJ. You won’t need to install any plug-in separately to run Kotlin programs.

Follow these steps to create and run a new Kotlin project in IntelliJ

  1. Create a new project by selecting “Create New Project” on the welcome screen or go to File → New → Project.Select Kotlin on the left side menu and Kotlin/JVM from the options on the right side –IntelliJ Kotlin Project Setup
  2. Specify the project’s name and location, and select a Java version (1.6+) in the Project SDK. Once all the details are entered, click Finish to create the project –IntelliJ Kotlin Hello World Project ExampleThe generated project will look like this –IntelliJ Kotlin Hello World Project Directory Structure
  3. Let’s now create a new Kotlin file. Right click on src folder → New → Kotlin File/Class.IntelliJ Create new Kotlin ClassA prompt will appear where you’ll need to provide a name for the file. Let’s name it HelloWorld.
  4. Now let’s write a simple hello world program in the new file that we have created. Add the following main() function to the HelloWorld.kt file –Kotlin Hello World Example
  5. Finally, You can run the program by clicking the Kotlin icon that appears beside the main() method –Run Kotlin Program in IntelliJ IDEA

You can also run the program by Right Clicking the HelloWorld.kt file and selecting Run 'HelloWorldKt'.

Setting up Kotlin in Eclipse

I assume that you have Eclipse installed on your system. If not, download the eclipse installer from Eclipse Downloads page, and install “Eclipse IDE for Java Developers”.

Once Eclipse is installed, follow the steps below to setup and run Kotlin in Eclipse –

  1. Install Kotlin Plugin from Eclipse Marketplace: Go to Help → Eclipse Marketplace, and search for Kotlin.Eclipse Kotlin PluginClick install to install the plugin.
  2. You will need to restart eclipse once the installation is finished.
  3. Let’s verify the plugin’s installation switching to Kotlin perspective in eclipse. Go to Window → Perspective → Open Perspective → Other. A window will open which will show Kotlin as a new perspective. Select Kotlin and click Open to open Kotlin perspective –Eclipse Kotlin Perspective
  4. Let’s now create a new project. Select File → New → Kotlin Project. Enter the project’s name and click finish –Create Kotlin Project in Eclipse
  5. A new project will be created which will look like this –Eclipse Kotlin Project Directory Structure
  6. Let’s now create a new Kotlin file under the src folder. Right click src folder → New → Kotlin File –Create Kotlin File/Class in Eclipse
  7. First, add the following code in the HelloWorld.kt file, and then right-click anywhere on the source file and click Run As → Kotlin Application to run the application –Run Kotlin Application in Eclipse

Conclusion

That’s all folks! In this article, you learned about Kotlin and some of its features. You also learned how to setup and run Kotlin programs in your system using Kotlin’s standalone compiler and IDEs like IntelliJ and Eclipse.

Kotlin Operators with Examples
07
Mar
2021

Kotlin Operators with Examples

In the previous article, you learned how to create variables and what are various basic data types available in Kotlin for creating variables.

In this article, you’ll learn what are various operators provided by kotlin to perform operations on basic data types.

Operations on Numeric Types

Just like other languages, Kotlin provides various operators to perform computations on numbers –

  • Arithmetic operators (+-*/%)
  • Comparison operators (==!=<><=>=)
  • Assignment operators (+=-=*=/=%=)
  • Increment & Decrement operators (++--)

Following are few examples that demonstrate the usage of above operators –

var a = 10
var b = 20
var c = ((a + b) * ( a + b))/2   // 450

var isALessThanB = a < b   // true

a++     // a now becomes 11
b += 5  // b equals to 25 now

Understanding how operators work in Kotlin

Everything in Kotlin is an object, even the basic data types like IntCharDoubleBoolean etc. Kotlin doesn’t have separate primitive types and their corresponding boxed types like Java.

Note that Kotlin may represent basic types like IntCharBoolean etc. as primitive values at runtime to improve performance, but for the end users, all of them are objects.

Since all the data types are objects, the operations on these types are internally represented as function calls.

For example, the addition operation a + b between two numbers a and b is represented as a function call a.plus(b) –

var a = 4
var b = 5

println(a + b)

// equivalent to
println(a.plus(b))

All the operators that we looked at in the previous section have a symbolic name which is used to translate any expression containing those operators into the corresponding function calls –

ExpressionTranslates to
a + ba.plus(b)
a – ba.minus(b)
a * ba.times(b)
a / ba.div(b)
a % ba.rem(b)
a++a.inc()
a−−a.dec()
a > ba.compareTo(b) > 0
a < ba.compareTo(b) < 0
a += ba.plusAssign(b)

You can check out other expressions and their corresponding function calls on Kotlin’s reference page.

The concept of translating such expressions to function calls enable operator overloading in Kotlin. For example, you can provide implementation for the plus function in a class defined by you, and then you’ll be able to add the objects of that class using + operator like this – object1 + object2.

Kotlin will automatically convert the addition operation object1 + object2 into the corresponding function call object1.plus(object2) (Think of a ComplexNumber class with the + operator overloaded).

You’ll learn more about operator overloading in a future article.

Note that the operations on basic types like IntCharDoubleBoolean etc. are optimized and do not include the overhead of function calls.

Bitwise Operators

Unlike C, C++ and Java, Kotlin doesn’t have bitwise operators like |(bitwise-or), &(bitwise-and), ^(bitwise-xor), << (signed left shift), >>(signed right shift) etc.

For performing bitwise operations, Kotlin provides following methods that work for Int and Long types –

  • shl – signed shift left (equivalent of << operator)
  • shr – signed shift right (equivalent of >> operator)
  • ushr– unsigned shift right (equivalent of >>> operator)
  • and – bitwise and (equivalent of & operator)
  • or – bitwise or (equivalent of | operator)
  • xor – bitwise xor (equivalent of ^ operator)
  • inv – bitwise complement (equivalent of ~ operator)

Here are few examples demonstrating how to use above functions –

1 shl 2   // Equivalent to 1.shl(2), Result = 4
16 shr 2  // Result = 4
2 and 4   // Result = 0
2 or 3    // Result = 3
4 xor 5   // Result = 1
4.inv()   // Result = -5

All the bitwise functions, except inv(), can be called using infix notation. The infix notation of 2.and(4) is 2 and 4. Infix notation allows you to write function calls in a more intuitive way.

Operations on Boolean Types

Kotlin supports following logical operators for performing operations on boolean types –

  • || – Logical OR
  • && – Logical AND
  • !   – Logical NOT

Here are few examples of logical operators –

2 == 2 && 4 != 5  // true
4 > 5 && 2 < 7    // false
!(7 > 12 || 14 < 18)  // false

Logical operators are generally used in control flow statements like ifif-elsewhile etc., to test the validity of a condition.

Operations on Strings

String Concatenation

The + operator is overloaded for String types. It performs String concatenation –

var firstName = "Rajeev"
var lastName = "Singh"
var fullName = firstName + " " + lastName	// "Rajeev Singh"

String Interpolation

Kotlin has an amazing feature called String Interpolation. This feature allows you to directly insert a template expression inside a String. Template expressions are tiny pieces of code that are evaluated and their results are concatenated with the original String.

A template expression is prefixed with $ symbol. Following is an example of String interpolation –

var a = 12
var b = 18
println("Avg of $a and $b is equal to ${ (a + b)/2 }") 

// Prints - Avg of 12 and 18 is equal to 15

If the template expression is a simple variable, you can write it like $variableName. If it is an expression then you need to insert it inside a ${} block.

Conclusion

That’s all folks! In this article, you learned what are various operators provided in Kotlin to perform operations on Numbers, Booleans, and Strings. You also learned how the expressions containing operators are translated to function calls internally.

As always, Thank you for reading.

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.