Android Application
Development with
Kotlin

Build Your First Android App in No Time

by

Hardik Trivedi

FIRST EDITION 2020

Copyright © BPB Publications, India

ISBN: 978-93-89423-501

All Rights Reserved. No part of this publication may be reproduced or distributed in any form or by any means or stored in a database or retrieval system, without the prior written permission of the publisher with the exception to the program listings which may be entered, stored and executed in a computer system, but they can not be reproduced by the means of publication.

LIMITS OF LIABILITY AND DISCLAIMER OF WARRANTY

The information contained in this book is true to correct and the best of author’s & publisher’s knowledge. The author has made every effort to ensure the accuracy of these publications, but cannot be held responsible for any loss or damage arising from any information in this book.

All trademarks referred to in the book are acknowledged as properties of their respective owners.

Distributors:

BPB PUBLICATIONS

20, Ansari Road, Darya Ganj

New Delhi-110002

Ph: 23254990/23254991

MICRO MEDIA

Shop No. 5, Mahendra Chambers,

150 DN Rd. Next to Capital Cinema,

V.T. (C.S.T.) Station, MUMBAI-400 001

Ph: 22078296/22078297

DECCAN AGENCIES

4-3-329, Bank Street,

Hyderabad-500195

Ph: 24756967/24756400

BPB BOOK CENTRE

376 Old Lajpat Rai Market,

Delhi-110006

Ph: 23861747

Published by Manish Jain for BPB Publications, 20 Ansari Road, Darya Ganj, New Delhi-110002 and Printed by him at Repro India Ltd, Mumbai

Dedicated to

All the fervent learners
who flourish to explore and learn for knowledge building

About the Author

Hardik Trivedi is a computer programmer and a self-taught Android application development. He started Android application development back in 2010. He is a Kotlin enthusiast and an active community speaker. He actively contributes to Stack overflow and also writes a blog. An Android application developer on his job, he has worked in numerous domains, for example, consumer internet, sports, banking, entertainment. He also mentors college students and professionals who want to develop their career in Android application development. As an active contributor in the community, he has delivered speeches for events hosted by GDG (Google Developer Group). He is already a co-author of “Kotlin Blueprints” – A book that explains how Kotlin can be used everywhere.

In his personal life, he loves to travel, paint, and cook. In his retirement days, you may find him owning a restaurant and making lip-smacking food for his customers.

Acknowledgement

All innovation can not be done without unpredicted hurdles. To overcome this and continue with the research along with the knowledge, you must need the support of supreme powers. I thank the supreme power of God, who kept me motivated enough to work on this book in my spare time.

My family kept me courageous enough for months. I was stealing time from my family and used to write books on those hours. I acknowledge their sacrifice. My sincere thanks to them.

I would also like to take the opportunity to thank many other contributors in the community whose research helped me to extend my research work. A salute to all those developers.

Last but not least, I thank BPB publications, who were involved in this book. Without them, it would not have been possible.

Preface

This book aims to provide the knowledge around the fundamental concept of Kotlin languages, and it’s an application in Android application development. It covers basic to advanced concepts with practical examples. Each chapter in this book is a step by step journey towards the learning Kotlin and excel in various topics and concepts. It covers topics like data types, various functions, including lambdas and higher-order functions. It also covers advance topics like Generics, Collections, DSL, Coroutine, etc.

Most importantly, such concepts are explained with practical usage of it in Android application. You will get to know what is the best possible way to use these concepts while you develop an Android application. In this book, along with Kotlin, an attempt has been made where few Android-specific topics are also explained. For example, the application is using Architecture components, including ViewModel, LiveData, NavigationComponent, and also it uses Flow, which is a hot topic in Kotlin.

Readers with little previous knowledge of Android application development can easily follow this book. Most of the chapters are code-heavy and focuses on practical usage of Kotlin’s features. Each chapter has code on the GitHub. You can checkout this code and try it out. Or you can develop in parallel and cherry-pick things from the sample code base as and when you need it. Few chapters also follow the quiz at the end, and you can self assess yourself by going through that quiz. In total ten chapters, you will learn the following:

Chapter 1 focuses on setting up the development environment. You configure the tools and environment which you will need to learn and develop Android application. Also, you learn some very basics of Android, e.g., Android Components and how to work with those components. You will become familiar with Kotlin code style in this chapter. In the end, a simple “HelloWorld” Android application in Kotlin will be developed.

Chapter 2 takes on the voyage of learning Kotlin. You learn basics in this chapter. This chapter covers basics about data type and variables, function, string manipulation, control flow. It covers various types of functions including, inline and infix function. Everything is well explained by small yet practical examples. After functions, the chapter covers the concept of classes and properties. What differences Kotlin class as compare to Java class is well explained. It also has lots of tips and notes around IntelliJ IDEA. Error handling is also an attractive section in this chapter, which demonstrates how try-catch works in Kotlin. Scoping functions are very generously written and aim to give insight into it. And the most exciting part of this chapter is a quiz section. A total of 10 questions is designed, which will give a quick recap of the entire chapter in the end.

Chapter 3 is all about diving deep into the advance topics of Kotlin. The type of system delegated properties, lambdas, collections, and generics. You think of a topic, and it’s covered one or the other way in this chapter. It also covers DSL and annotations. This chapter is once again full of code snippets, tips, diagrams. In collections, only the topics which are specific to Kotlin are covered. The basics of the collection are the same as Java, so we have focused on the delta part. Generics also explain covariance and contravariance’s topic. And one more time total of 10 questions are designed, which will give the quick recap of the entire chapter in the end.

Chapter 4 introduces you to the most loved topic of software development, i.e., Design Patterns. The chapter covers a few of the design patterns. Kotlin handles a few design patterns well, e.g., Builder, Singleton, Decorator, Facade, Observer, and Chain of responsibility. A few of the Kotlin features are used to implement these design patterns in Kotlin’s way. A short but exciting chapter is covering the practical aspect of it.

Chapter 5 takes you into the working world of Kotlin and Android. Along with the development skill, this chapter also focuses on some other software development aspects. e.g., Requirement analysis, user story mapping, etc. These agile development approaches will help you in your day to day job. One must acquire these skills. The chapter explains the meal recipe application, which you will be developing as a learning project.

Along with requirement gathering for the meal recipe app, we also design layouts as well. Also, this chapter covers a new dependency injection framework named koin. A bit of styling and theming is also covered in this chapter. A right amount of writing has been made to explainthe general architecture and skeleton of the application. Topics like Repository, LiveData, ViewModel, and Navigation component are discussed here.

Chapter 6 is dedicated to connecting the application to the remote server. We bring the data from the server to the app. The repository layer of the app is in the center of the chapter. To make network calls since we are using coroutine, we have also discussed a concept about coroutine. Also, a very new topic at the time of writing the book is Flow. Flow is Kotlin’s attempt to manage the asynchronous data stream. Flow is well discussed here. Just go with the Flow!

Chapter 7 is reaching towards the end of the development of an Android application. Once we connect the application to the remote server, now this chapter focus on Kotlin-izing the remaining app. We give a bit of Kotlin touch in all other classes which we have created so far. ViewModels, LiveDatas, Activities, and Fragments, all classes are Kotlin-ized. Along with Kotlin’s features, we have discussed the error handling layer in this chapter. You also get to know how you can write your project’s build.gradle files into Kotlin using the concept of DSL.

Chapter 8 is dedicated to testing. An essential aspect of software development is many times neglected. Kotlin offers some super cool syntax to make nit testing fun. The chapter also covers the usage of jUnit5. Yes! A great effort has been made to do jUnit5 works for writing all unit test cases. It conceptually explains the testing and covers the Android recommended testing pyramid

Chapter 9 is all about making your application production-ready. You have to be well prepared with things like reporting tools, documentation, etc. This chapter focuses on all such aspects. Some of the features which Kotlin offers for safety are discussed, which will help your code not to malfunction on production. A static code analysis tool detects, which will do the job for you to find possible bugs and performance issues, cyclomatic complexity, and code smells. Dokka is another tool that is discussed in this chapter. Dokkahelps you to generate the documentation which will help your manager and colleagues.

Chapter 10 gives you an idea about the Kotlin ecosystem. You will get a brief idea about how Kotlin can be used to write Web apps and iOS applications. A practical example has been given, which shows how code sharing can be done between Android and iOS. At the end of the chapters, some cool open-source projects and libraries are mentioned, which can make your development faster and secure.

Downloading the code
bundle and coloured images:

Please follow the link to download the
Code Bundle and the Coloured Images of the book:

https://rebrand.ly/qh02x40

Errata

We take immense pride in our work at BPB Publications and follow best practices to ensure the accuracy of our content to provide with an indulging reading experience to our subscribers. Our readers are our mirrors, and we use their inputs to reflect and improve upon human errors if any, occurred during the publishing processes involved. To let us maintain the quality and help us reach out to any readers who might be having difficulties due to any unforeseen errors, please write to us at:

errata@bpbonline.com

Your support, suggestions and feedbacks are highly appreciated by the BPB Publications’ Family.

CHAPTER 1

Getting Started with Kotin for Android

“Any fool can write code that a computer can understand. Good programmers write code that humans can understand.”

– Martin Fowler

Many of us very much believe in the above quote. And might be wondering if the above statement is true why there is no programming language following this quote. With Kotlin your quest will come to an end. Welcome to the world of Kotlin.

Those who are still new to Kotlin, for them I would describe Kotlin as,

A modern language to meet the modern age requirements.

Very expressive and no verbosity.

A statically typed language with a top-notch type of safety. For example, Null safety.

And 100% interoperable with Java.

Kotlin as a language has a plethora of features to offer an abundance of applications. You can completely rely on this language and develop your application in Android, JavaFX, Spring boot, JavaScript, Node.js even iOS and embedded systems.

Structure

Setting up an environment for Android app development

Configure Kotlin and make it run with Android Studio and Gradle

“Hello Kotlin” program in Android written in Kotlin

Brush up on Android basics

Writing Android applications in Kotlin is pure bliss. And with this, some of the coolest start-ups to Fortune 500 companies will agree. Image Credit - https://developer.android.com/kotlin (Data as on 1st July 2019):

Figure 1.1

Kotlin is owned by the IDE (Integrated Development Environment) giant JetBrains s.r.o. They are the best in class as far as IDEs are concerns. And with the above companies using it, you can see that Kotlin is in the safe hands of great open source developers’ community. And it surely has a bright future. Kotlin is free and open-source under the Apache 2.0 license. JetBrains unveiled this project Kotlin back in 2011 and after that hardworking team was working on it to make it robust and rich with features. In February 2016 finally, the team released Kotlin v1.0. This was the first official stable release.

According to PYPL (PopularitY of Programming Language) index, Kotlin has made it to the top 15 in countries like India, United Kingdom, France, and Germany. And in the United States, it has secured rank 16. (The data has compiled against June 2019 as per http://pypl.github.io/PYPL.html)

During the annual developer conference, I/O-17 Google has announced its official support to Kotlin. From that day Kotlin has become the official language for Android application development. Google has also promised that for all documentations, tutorials, blogs, and code references on the developer website they are going to prefer Kotlin first. From the day of the announcement and to current day Kotlin has gained so much popularity in the Android developer community. In this chapter, you will soon realize the potential of this language and see how easy it is to start using it with its fantastic tooling support with Android Studio.

How does the Kotlin code look like?

In following Figure 1.2, some of the coolest features of Kotlin are highlighted.

Figure 1.2

You can see how lambdas are making the callbacks looks natural. Basically whatever you are seeing inside those curly braces is nothing but a function. A function passed as the last parameter can take the form of such curly braces and make the code look natural as if it is executed as a body of launch function.

Another feature that is emphasized here is named arguments, where you can bind a parameter along with its name at the time of calling it. With named arguments, you can take the liberty of changing order as well. This feature makes a lot of sense when there is a function with a lot of parameters. You can easily understand the parameters and their purpose just by looking at the function call.

For hardcore Java developer, a de novo feature is string interpolation. It’s a simple feature but incredibly useful when readability comes to a picture. You can easily concatenate strings by using $ sign in double-quotes.

Last but not least no semicolons (:).

How does Kotlin build process work with Android?

If you are a seasoned Android developer who knows about how Android build system works will surely have a question - Do I need to learn anything else as well to make Kotlin work with Android and build an APK file?

Answer The answer is NO. Just by knowing Koltin is enough. All your knowledge about the build system still stands true. Kotlin is a JVM language, this means if your existing code is working with JVM the Kotlin code will work as well with no changes in your build process. Kotlin’s output is also the bytecode, in short, a .class file. Any JVM can interpret the .class file and can process that and derive the instructions written inside it.

JetBrains IDE will come with Kotlin’s compiler by default. An IDE will compile your Kotlin code and produce the bytecode. For Android, the Android Studio has first-class support for Kotlin from version 3.0 onwards. Both Dalvik (A just in time compiler) and ART (An ahead of time compiler) can perceive bytecode. So for the Android build system, it doesn’t matter that the code is written on Kotlin or Java. Android doesn’t even know that code is written in which of these languages.

Refer to below Figure 1.3 to get more clarity around how Kotlin code can be used by Android and have no side effects at all:

Figure 1.3

Configure Kotlin in Android Studio

It was a dream come true for the Android developer community when Google announced the Android Studio as the official IDE for Android application development. It has evolved so beautifully that any feature you can think of or ask is there already. The Android Studio is amazingly customized for the need of the developer community. This makes application development fast and easy.

When Kotlin was announced as the official language for Android app development, there was no out of the box support for Kotlin. Developers have to perform a few steps to configure Kotlin into their Android project.

Note: If your Android Studio version is 3.0 or above skip this section. But if your Android Studio version is below 3.0 and does not want to upgrade to version 3.0 or above, you need to follow the below steps.

Note: If your Android Studio version is 3.0 or above skip this section. But if your Android Studio version is below 3.0 and do not want to upgrade to version 3.0 or above, you need to follow below steps. Installing Android Studio

Throughout the learning and application development, we will only and only use Android Studio. If you are not having Android Studio on your computer please below steps will help you to get it installed.

Downloading and installing Java

Android still relies heavily on Java Development Kit (JDK). Nowadays Android Studio comes with the latest OpenJDK embedded into the installer only to provide a jump start to entry-level developers. But it is always nice to have the latest version of JDK installed. Java has done a few releases in the last 2-3 years. While writing this book the latest release was Java 12. But for Android, it’s recommended to have JDK 8 on your computer. Please download the JDK 8 from link https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html

Follow these instructions to install Java on your Microsoft’s Windows or Apple’s Mac computers, or Linux machines. https://java.com/en/download/help/download_options.xml

Downloading and installing Android Studio

Get the latest stable version of Android Studio from https://developer.android.com/studio. The page will automatically detect your OS and suggest either Windows, Linux, or Mac-based installers. Android Studio requires a slightly high configuration compared to standard computers. The above-mentioned page also has a section about system requirements. Please make sure your computer is matching the system requirement to ensure good development and learning experience.

Google and Android Studio team has done a pretty amazing job right from providing help to install them on any machines. This link https://developer.android.com/studio/install is always up to date with the installation video against the latest releases.

Just follow the above video and get the Android Studio installed on your machine.

Installing Kotlin plugin

Below Android Studio 3.0 the only way to use Kotlin was to install a plugin.

Go to File | Settings | Plugins | Install JetBrains plugin and search for Kotlin and install it.

And if you are Welcome to Android Studio screen, choose to Configure | Plugins | Install JetBrains plugin and then search for Kotlin and install it.

Tip: While installing Android Studio if there is no Android SDK found, the wizard will ask you to install the Android SDK. At this moment you should see two options Standard and Custom. Always select the Standard option if you are unsure and you can always customize it later installation as well.

After installation Android Studio needs to be restarted. The search plugin window will look like below Figure 1.4:

Figure 1.4

Updating build.gradle

As a next step, we will add classpath and apply plugin into both project and app level build.gradle file.

Add classpath into the project’s build.gradle

Add the blow highlighted code as classpath in build.gradle. Whatever defined inside project level build.gradle will be shared by all modules within the project. If it’s a multi module project all project can have Kotlin:

buildscript {

ext.kotlinVersion = “1.3.31”

ext.supportLibVersion = “26.0.0”

repositories {

jcenter()

}

dependencies {

classpath ‘com.android.tools.build:gradle:3.4.1’

classpath “org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion”

classpath “org.jetbrains.kotlin:kotlin-android-extensions:$kotlinVersion”

}

}

Add Koltin as a library

After Kotlin defined in the classpath, you need to add Kotlin as a library into app level build.gradle. Add the lines which are highlighted with bold fonts into your app level build.gradle file:

apply plugin: ‘com.android.application’

apply plugin: ‘kotlin-android’

apply plugin: ‘kotlin-android-extensions’

android {

// … various gradle setup

}

dependencies {

compile fileTree(dir: ‘libs’, include: [‘*.jar’])

testCompile ‘junit:junit:4.12’

compile “com.android.support:appcompat-v7:$supportLibVersion”

compile “com.android.support:recyclerview-v7:$supportLibVersion”

compile “org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version”

}

If and only if you specify the above code in your build.gradle file will enable you to write Kotlin code in your Android application development.

Note: Depending upon the version of your Android Studio and your Gradle system you may be prompted to replace compile to implementation.

Create a new project

After the installation, plug-in setup you are all set to create a new project and go on expeditions for your Kotlin learning. Adventure, learning, and fun are guaranteed in each and every section now onwards.

As a part of learning and creating something meaningful, we have selected The recipe app as a project. We will create an app that covers all the scenarios any Android developer can face during their real project work. We will dig down more to understand the scope of this application in the coming chapter. For now, we will just proceed with project creation. We will name our project The International Dhaba:

Launch Android Studio and you should see something like Figure 1.5:

Figure 1.5

Select the type of Activity you need. For now, we will just go ahead with blank activity as an option. We do not need any complicated navigation or interface. See Figure 1.6:

Figure 1.6

Configure a project. As a next step enter the project name and select other details exactly like Figure 1.7 as described below. If you are using Android Studio older than version 3 you will not see the language selection option and you need to set up Kotlin manually. Follow the Configure Koltin in Android Studio section:

Figure 1.7

Setting up Android emulator

Now the project has been set up, the next step is to figure out how we will run the Android projects. As an Android developer, you can run your project on both emulator and real device without any trouble. Android SDK will allow to set a variety of emulators to test your application. You can set up emulators based on your requirements, for example, API level, hardware, screen density, memory, storage configuration, network latency, even architecture of your OS. Emulators are a very powerful, robust and efficient way to test your application. Android calls them virtual devices. The developer portal has step by step guide on setting up emulators or AVDs on your computer. Please follow https://developer.android.com/studio/run/managing-avds completely to set up emulators which suits your needs.

Recommended configurations for this book and project:

Device: Pixel 3

OS Android 9 or API level 28

Screen resolution: 1080 x 2160

CPU architecture: x86_64

RAM: 1024 MB

There will be other details while setting up emulators, for example, Heap size, SD card memory, and more. You can keep as it is.

Tip: Android emulators are sometimes slow. To get the best performance on an emulator, make sure you select x86 CPU architecture. Also, you should install the Intel x86 Emulator Accelerator (HAXM) on your computer. This will make a visible difference while interacting with an emulator.

Display “Hello Kotlin”

Enough of theory! Where is my Kotlin code? How do I just start writing my Android application in Kotlin? I am sure you want the answer to this question. This section will surely make you happy. In this section, you will get a gist of Kotlin code. You will get that feeling how amazing it is to write an application using Kotlin.

If you are using Android Studio older than 3.0 you will have MainActivity.java in your src directory under your main package. This means you need to convert this file into Kotlin file to start writing the code.

To convert the Java code into Kotlin code, Android Studio has in build converter:

Open MainActivity.java.

Go to the menu Code.

Select the Convert Java file to Kotlin file option.

On successful completion of the above three steps you should have MainActivity.kt and you should see the Kotlin code instead of Java.

For Android Studio 3.0 and above, after the project setup is completed you will already have MainActivity.kt inside your src directory under your main package.

Open activity_main.xml from res/layout directory and put below content inside it:

<?xml version=”1.0” encoding=”utf-8”?>

<androidx.constraintlayout.widget.ConstraintLayout

xmlns:android=”http://schemas.android.com/apk/res/android”

xmlns:app=”http://schemas.android.com/apk/res-auto”

android:layout_width=”match_parent”

android:layout_height=”match_parent”>

<TextView

android:id=”@+id/displayTextView”

android:layout_width=”0dp”

android:layout_height=”wrap_content”

app:layout_constraintLeft_toLeftOf=”parent”

app:layout_constraintRight_toRightOf=”parent”

app:layout_constraintTop_toTopOf=”parent”

android:layout_marginTop=”16dp”

android:layout_marginLeft=”16dp”

android:layout_marginRight=”16dp”/>

<Button

android:text=”Click”

android:layout_width=”0dp”

android:layout_height=”wrap_content”

android:id=”@+id/clickButton”

app:layout_constraintTop_toBottomOf=”@+id/displayTextView”

android:layout_marginTop=”16dp”

app:layout_constraintStart_toStartOf=”parent”

android:layout_marginStart=”16dp”

app:layout_constraintEnd_toEndOf=”parent”

android:layout_marginEnd=”16dp”/>

</androidx.constraintlayout.widget.ConstraintLayout>

Go to MainActivity.kt file. Here we will do a very ordinary exercise. We will set a click listener on clickButton and try to display Hello Kotlin on displayTextView TextView. The code to achieve this can look like below:

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_main)

clickButton.setOnClickListener {

displayTextView.text=”Hello Kotlin”

}

}

}

The highlighted code snippet does the job. Sounds interesting, isn’t it?

On the other side Java code can be these verbose and full of Android ceremonies:

public class SplashActivity extends AppCompatActivity {

private Button buttonClick;

private TextView displayTextView;

@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

buttonClick = findViewById(R.id.clickButton);

displayTextView = findViewById(R.id.displayTextView);

buttonClick.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View view) {

displayTextView.setText(“Hello Kotlin”);

}

});

}

}

Concise and conveying the most this is the motto of Kotlin and they are making it work for this ordinary problem as well.

Now run the project by clicking on the green play ▶ icon in your Android Studio. Select any of the emulators which you have created before. In a later chapter while we will also learn how to connect and run the application on the real device.

Understanding the basics of Android

Android is a highly mature platform. Behind this great platform, there is hard work for some of the greatest engineers and strategists. We presume you have a decent amount of hands-on knowledge around developing an Android application. We will be using some of the very basic Android components and APIs during our learning and throughout this book. Hence it’s important to know them well in advance. In the previous section Display Hello Kotlin we referred the terminology like Activity. The Activity has a very specific meaning in Android. It’s one of the core components provided by the Android platform and through this only you can create any user interactive screen. No user interaction is possible without the Activity in your application.

I will shed some light on the component we will be using in our application development process.

Activity

The significance of Activity in Android application is crucial. It will serve as a gateway to your application. Unlike the traditional programming approach, Android does not have a main method to act as an entry point. It is this Activity which will act as an entry point. After user taps on your application icon on the launcher screen, your application is loaded and Android will look for the Activity you have defined as an entry point of. On finding matching appropriate Activity the UI becomes visible to the user.

To display any kind of element on screen your class must extend Activity or any valid child of Activity class, that is, FragmentActivity, AppCompatActivity, and more. Technically speaking Activity provides you a window to draw your UI on it. Now your application can have the requirement to show multiple screens, so your application can have multiple activities. Android system is designed in such a way that any application can launch any Activity of any other screen. Having said that this is only possible if a way by application developer itself. For example, you are using WhatsApp and want to store a contact number from which you have just received a message. You tap on Create contact and your phone’s contact screen is opened with some prefilled number. This means your WhatsApp is opening your phone’s create contact Activity.

Activity lifecycle

Activity is a special class and component in the Android platform. And the developer has to adhere to some contract to make sure the application and user interface behave expectedly. This contract is known as the Activity lifecycle. I will try to explain some of the very important lifecycle methods which work as a callback to your application. It will announce the important state changes in your view. Hence very important!

onCreate(): The onCreate() method will be called when Activity is completely created by OS and ready for further input from the user. As a developer, we must override this method. The developer usually creates the view and binds the view element to objects.

onStart(): When activity about to come to the foreground, the on Start() method is called. This is usually immediately called after on Create(). After this method is called Activity becomes visible to the user.

onResume(): Finally the activity is fully visible to the user and ready for interaction. Whenever this method is called for any activity this means the activity is on top of the task stack.

onPause(): Whenever this method is called, it means Activity is partially visible and the user is about to leave that activity.

onStop(): The reason for onStop() being called is, activity is in the background and no longer visible to the user. When you navigate from one Activity to another Activity, new Activity gets created and your previous Activity goes in onStop mode. You can free up your resources or stop some processes, for example, listening to location, stopping an ongoing network request.

onDestroy(): This is the final call to your Activity class. This indicates that Activity is about to destroy. OS will free all your resources occupied by the Activity.

Intent

Android has components like Activity, Service, Broadcast Receiver, and Content providers. The intent is the message passing mechanism between these components. Imagine where you want to start the Activity and want the Activity to work on some data created by previous Activity. As the name suggests Intent class holds the intent or purpose to start the new Activity or any other component.

Intent can be of two types, which are explained in the following section.

Implicit Intent

For implicit intent, it doesn’t rely on the name of a component to be started. Instead, it depends on the action. Android OS will present all possible components which can handle that intent. Following code demonstrates how you can open an application which is capable to send an email. This code uses concept of implicit intent:

val emailIntent = Intent(android.content.Intent.ACTION_SEND).apply {

/* Pass the required data */

type = “plain/text”

putExtra(android.content.Intent.EXTRA_EMAIL, arrayOf(“someemailid@whatever.com”))

putExtra(android.content.Intent.EXTRA_SUBJECT, “Subject goes here”)

putExtra(android.content.Intent.EXTRA_TEXT, “Message goes here”)

}

/* Pass this action to Android and whichever activity can handle this will respond */

startActivity(Intent.createChooser(emailIntent, “Send an email…”))

Explicit Intent

As the name suggests, the developer has to specify the name of the component explicitly. You typically use explicit intent in a controlled and know environment (that is, your app) where you know the name of the component.

Assume you want to start a new Activity called HomeActivity from your current Activity, you can write an explicit intent to start activity like this:

val intent = Intent(this@SplashActivity, HomeActivity::class.java)

intent.putExtra(“message”, “Hello from SplashActivity”)

startActivity(intent)

And SplashActivity can read this message field by getting and unwrapping an intent:

val message=getIntent().extras.getString(“message”,”No message”)

The getString function for extras object accepts two parameters: A key and a default message. No message will be returned as default message if no value found for key messages.

Building Intent

Being a message passing mechanism, it holds certain data so that Android can understand which component to start. To build an Intent you need the following data:

Component name: Name of a component for example, HomeActivity or SyncService.

Action: A simple string defined as an action, for example, ACTION_VIEW, ACTION_SEND, and more.

Data: Data to be processed.

Category: Category of a component that handles the intent.

Extras: Additional data in the form of key-value pairs.

Flag: A Metadata for Android about the launching style of the component. This applies to only the Activity component.

AndroidManifest.xml

A must have file for any type of Android application. An android project cannot be built and installed without the AndroidManifest.xml file. It’s the manifesto of your application. This must be named exactly as AndroidManifest.xml. No other name and no other format than XML is allowed. It contains some crucial information like:

The unique identifier for your application known as a package.

List of the component which your application supports. You need to register all your activities, services, broadcast receivers, and content providers with their name and all required metadata.

Type of permission your application can require during runtime.

Application’s name and icon.

Supported screen sizes.

It can look like below:

<?xml version=”1.0” encoding=”utf-8”?>

<manifest xmlns:android=”http://schemas.android.com/apk/res/android”

xmlns:tools=”http://schemas.android.com/tools”

package=”com.hardiktrivedi.theinternationaldhaba”>

<uses-permission android:name=”android.permission.INTERNET”/>

<uses-permission android:name=”android.permission.WRITE_EXTERNAL_STORAGE”/>

<application

android:allowBackup=”true”

android:icon=”@mipmap/ic_launcher”

android:label=”@string/app_name”

android:roundIcon=”@mipmap/ic_launcher_round”

android:supportsRtl=”true”

android:theme=”@style/AppTheme”

tools:ignore=”GoogleAppIndexingWarning”>

<activity

android:name=”.SplashActivity”

android:theme=”@style/Theme.AppCompat.Light.NoActionBar.FullScreen”>

<intent-filter>

<action android:name=”android.intent.action.MAIN”/>

<category android:name=”android.intent.category.LAUNCHER”/>

</intent-filter>

</activity>

<meta-data

android:name=”preloaded_fonts”

android:resource=”@array/preloaded_fonts”/>

</application>

</manifest>

Fragment

Creating an activity is an expensive call to CPU and memory. The biggest advantage with Activity is the life cycle aware view, which leads to substantial control over behavior. So, the Android team thought to define a component that has reusability and has its lifecycle events. This ideology gave birth to Fragment. Fragment is a widely used component now. Each fragment requires an Activity to hold itself. A Fragment can be attached to multiple activities at any given time.

To create a Fragment you must extend the Fragment class. Then comes the FragmentManager which has sole responsibility to add, replace, or remove from its parent Activity. Like Activity, Fragment also supports back stack navigation. Although managing Fragment’s backstack can be tricky sometimes.

In most cases, Fragment’s lifecycle is of similar fashion compared to the Activity lifecycle.

I highly recommend to take a look at https://github.com/xxv/android-lifecycle for utter understanding

A typical code to add a fragment can be something like:

val newFragment = ShowRecipeFragment()

val transaction = supportFragmentManager.beginTransaction()

transaction.replace(R.id.fragment_container, newFragment)

transaction.addToBackStack(null)

transaction.commit()

And a RecipeFragment view instantiation can look like this:

class ExampleFragment : Fragment() {

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,

    savedInstanceState: Bundle? ): View {

// Inflate the layout for this fragment

return inflater.inflate(R.layout.show_recipe_fragment, container, false)

}

}

Context

Context is one of the most used objects in Android. It’s an abstract term and each of the Android classes (Activity, Service, Application, Broadcast receiver) implements their version of Context. All these different implementations have completely divergent capabilities. Context gives information about the environment under which the application is running. In Android context can be obtained using getApplicationContext(), getContext(), getBaseContext(), or this:

Table 1.1: Capability of various contexts

An application can start an Activity from here, but it requires that a new task is created.

This is legal, but inflation will be done with the default theme for the system on which you are running, not what’s defined in your application.

Allowed if the receiver is null, which is used for obtaining the current value of a sticky broadcast, on Android 4.2 and above.

Dave Smith has given a detailed understanding that is shared at https://possiblemobile.com/2013/06/context. Take a good look.

Trying out Kotlin

The current stable version for Kotlin, while writing this chapter is v1.3.41. Kotlin’s team offers multiple ways to try out this fantastic language and provide you a jump start to learn this:

Kotlin is bundled by default with the Intellij IDEA community and ultimate edition.

Android Studio 3.0 onwards has the full support of Kotlin.

Eclipse IDE for Java Developers can support Kotlin. For this, you need to download the plugin from the marketplace.

It also comes with a standalone compiler. Here you can use any text file editor to write the code and build it using the terminal.

The most recommended way to getting your hands dirty in this language is using the playground on the browser, with no installation and no hassle.

The browser acts as a sandbox environment to learn Kotlin and provide support to all Kotlin features. Check out the following Figure 1.8 which is browser version of Kotlin playground:

Figure 1.8

Wrapping up

Along with the project set up, we learned some key Android concepts. All the concepts which are highlighted about can have a chapter-long description. They are real bones of Android application development.

If any of the concepts have made you think more about the behavior and scenarios, take some time and readout on the World Wide Web (WWW). We will use these terminologies and concepts throughout our application writing. The next few chapters will be focusing only and only on Kotlin language’s features only. We learn basics to some of the advanced concepts of Kotlin, including design patterns.

Throughout this chapter, we praised Kotlin and did all the preparatory work required to learn and practice it. Will it live up to your expectations?

CHAPTER 2

Kotlin Fundamentals

"The only way to learn a new programming language is by writing programs in it.”"

- Dennis Ritchie

The chapter exclusively focuses on the fundamentals of the Kotlin language. This covers basic concepts that will enable you to start writing the code in Kotlin. After this chapter, you will have good command on writing code. You will learn each and every topic in detail.

Structure

This chapter covers,

Understanding variables

Basics about function

Easy string handling and manipulations

Controlling program flow

Handling errors and exceptions

Playing around functions

Defining classes and properties

This chapter would be fun to learn as this covers very light yet important topics. You can practice the code in any of the IDEs, that is, IntelliJ IDEA, Eclipse, Playground on the browser as mentioned in Trying out Kotlin in Chapter 1: Getting started with Kotlin for Android. I would recommend using IntelliJ IDEA (Community edition 2019.1.3) only, throughout this chapter. We will stick to this IDE for a few more chapters. There is no need for any emulator or device to run the program. The console within IntelliJ IDEA is good enough to execute each and every program.

Let's quickly create a simple project named Kotlin Fundamentals. To create this open IntelliJ IDEA,

On welcome screen select – Create New Project option.

Select Kotlin as an option from the left panel:

Figure 2.1

Enter the project name - Kotlin Fundamentals and press Finish button:

Figure 2.2

Your plain Kotlin project should be created now and we are ready to roll.

Understanding basic types

The first thing to digest here for developers with Java background is that Kotlin does not have primitive data types. Everything is an object internally. So for any variable, you will have functions and properties associated with it. Although some special handling is done from Kotlin for some of the classes, for example, integers or float values, characters or Booleans to make them feel like primitive data types.

Numbers

Kotlin's team aims to make the developer's life easy, so they very much believe in a short learning curve. Keeping this point in mind Kotlin provides multiple types to represents the numbers. The classification is very close to Java language. Please refer below table to understand what numbers type are supported and how much bits they occupy in memory:

Type

Size in bits

Byte

8

Short

16

Int

32

Long

64

Float

32

Double

64

All the above type are classes but has private constructors, hence you cannot create the object using the constructor. All you can do is initialize them as literals. All types representing numbers extend an abstract class Number. All the subclasses of Number can hold signed and unsigned values. Below code describes how each and every type can be initialized and used:

val byte:Byte = 127

var short:Short = 32767

val int:Int = 2147483647

var long:Long = -922337203685477580L

val float:Float = 3.4028235E38F

var double:Double = 1.7976931348623157E308

fun main() {

println(byte)

println(short)

println(int)

println(long)

println(float)

println(double)

}

The coolest part is you can actually define numbers as hexadecimal and binaries. Try something like the following:

val hexBytes = 0xF_42_40

val bytes = 0b1111_01000010_01000000

fun main() {

println("Hexadecimal to decimal ${hexBytes.dec()}")

println("Bytes to decimal ${bytes.dec()}")

}

//Output

Hexadecimal to decimal 999999

Bytes to decimal 999999

Since all these types are a subset of Number, they all have few common functions to convert them to any other type and these ways you can convert them in top decimal, integer, float, double, etc.

The underscore (_) can be used to represent numbers in a group fashion. This is just to make the code look more natural and readable. They don't hold any value and stripped from the build. Lovely, isn't it?

String

The string is a very straight forward type to store a series of characters. Just like Java String is also an immutable type. This means once the value is assigned to any String object, it cannot be changed. In most of the cases String is initialized in the following way:

fun main() {

val simpleString = "Android application development with Kotlin"

val withNewLine = "\nA ready reckoner for your next Kotlin based Android application."

val multiLine = """This book will help you to

|build your Android application using Kotlin

|as a programming language""".trimMargin()

}

There are three different types of strings are getting initialized:

Using double quotes: The simplest and traditional way to initialize initialize your String.

With newline characters: Slightly uncommon way. There is a newline character as a value inside the String. Kotlin understands this and it will process this character and string will leave a newline before it’s actual data getting printed.

Multiline string: The most unusual way to initialize initialize a String. With using triple quotes, a String can hold a multiline data. Remember in Java this used to result into concatenated String initialization with + sign everywhere. It was making code so unreadable and unpleasant. Kotlin team knew this pain point and addresses it by using triple quotes initialization strategy. But there is a catch here. If you observe the code, you will find usage of pipe (|) operator and trimMargin() function. If you don't use it your string will also all the white spaces as their values. Their sizes can be different. Try below code in your IDE:

fun main() {

val multiLine = """This book will help you to

|build your Android application using Kotlin

|as a programming language""".trimMargin()

val multiLine1 = """This book will help you to

build your Android application using Kotlin

as a programming language"""

println(withNewLine.length)

println(multiLine1.length)

}

Booleans

A Boolean in Kotlin will always hold either true or false. See following code snippet which shows usage of Boolean:

fun main() {

val ok = true

var notOk = false

var maybe: Boolean? = null // Boxed to Boolean

}

Understanding variables

Name a programming language that does not have variables. The answer is NO, and Kotlin is no exception in it. But Kotlin will surely make your life easy. Kotlin has a classification of variables at any point in time they can be either a constant or a variable. There are two keywords val and var.

The val object represents any constants. Once the value is assigned to any val, it values cannot be changed anywhere in the program. They are your final objects. In Java, they used to represent the final keyword. If you try to change the value for any val, you will get a compile-time error in your code. val is immutable by default.

fun main() {

val dob // Doesn't work. Have to provide either value or type

val age = 30 // Works fine

age = 32 // This will not compile as val cannot be changed

val name: String //Allows declaration without value if type is specified.

name = "Hardik" // Only works while assigning value first time

name = "Hardik Trivedi" // Refer to the principle where val cannot be changed

}

Whereas var represents anything that can be changed later. Remember one thing, when we initialize the variable with var we have to either provide the type or the value. This means we can change the value anytime, but we cannot change the type of the variable:

fun main() {

var address = "Ahmedabad, India"

address = "London, UK" // var can be change anytime

var pinCode // Doesn't work. Have to provide either value or type

var pinCode = "EC1N 8EB"

pinCode = 380058 // Will not work, as you can only change the value and not the type

}

Tip: Try to declare your variable as Val wherever it is possible. This makes your code safe. You easily achieve thread-safe object by doing so. Also, there will be less surprises during runtime as the value and state of the object cannot be changed.

Type inference

Unlike Java, Kotlin supports the type inference feature. What does type inference mean? Well, this means, you do not need to specify the type of object while defining them. Kotlin has strongly typed language with this philosophy achieving type inference was something a must-have feature. If you refer a few of the above code snippet, you will notice that while defining variables we hardly specify the type of it. We never mention the type Int while defining age object. In the same way, we did not mention the type of address object as well. Not only for basic types, but type inference is also supported for any other complex type, for example, List, Array. Even for the user-defined type, for example, User, Recipe class type inference just works like a charm.

Note: From Java 10 onwards type inference is supported only for local variables. These are the variables that are used within any function. Kotlin supports this feature from its very first release.

Following code demonstrates how type inference works in Kotlin. Have a good look:

fun main(args: Array<String>) {

val phoneNumber = 1234567891 // Takes Int as a type

var isDataProcessed = false // Becomes Boolean which can be either true or false

val person = Person(

name = "Hardik Trivedi", age = 31,

address = Address(houseNumber = "7", streetName = "Lok kalyan marg", zipCode = "110001",

province = "New Delhi", country = "India")

)

}

data class Person(val name: String, val age: Int, val address: Address)

data class Address(val houseNumber: String, val streetName: String,

val zipCode: String, val province: String, val country: String)

In the above code snippet, there is no mention of any data type during declaration. It's visible how phoneNumber is taking Int as a data type. Similarly is Data Processed becomes Boolean and the person becomes an instance of Person class and so as Address. We can see the language behaving smartly and inferring the data type from the value itself.

Understanding the scope of the variable

Each and every defined variable has access limitations. Variables cannot be accessed like anything and anywhere. The visibility modifier or access specifier in Kotlin also affects how we access variables. We will cover the visibility modifier later on in this chapter.

Generally, a variable has a life span within the block (open and closed pair of curly brackets {}). Blocks can be of different types, for example, class, function, companion objects, loops, and more. Blocks can be nested as well. For example, a for loop inside the function is can be considered as a nested block. Observe the following code:

fun main() {

val outerNumber = 2

val function = {

val innerNumber = 20

println(innerNumber)

// outerNumber is accessible here and anywhere in the class

println(outerNumber)

}

for (count in 1..10) {

// outerNumber is accessible here and anywhere in the class

if (count % outerNumber == 0) {

println("$count is an even number")

} else {

println("$count is an odd number")

}

}

println(count)// count is not accessible outside for loop and result into compiler error

println(innerNumber)// innerNumber is not accessible outside for loop and result into compiler error

}

Naming a variable

Variable cannot start with a digit. The valid variable name may consist of letters, digits, and underscores. Take a look at some examples below:

fun main() {

val 1num = 10 //Error. Not valid

val num1 = 20 //Can contain number

val email_address = "email@provider.com" // Valid name but _ are not recommended

val @ddress = "London" //Error. No special characters are allowed except underscores '_'

val _classVariable = "Some value" // Valid

val sOMEgibBerisH = false // Valid name but not recommended

val selectedRecipe="Potato" // camelCase are perfect for naming a variable

//Below declaration is valid only if you have to name a variable to meet some outside dependency

val ‘val when var fun class’="Some value"

}

Kotlin advises to name variables in camelCase only. There can be scenarios where snake_case can be used. Also, Observe the last case where variable name consists of keywords in between backticks. In modern programming languages developer uses ORM library to map JSON to model classes. There can be a possibility that it can force the developer not to follow the convention. If JSON key matches with any of the Kotlin keywords, you can name it whatever you want but put them between backticks like shown in the above code snippet.

Functions in Kotlin

Functions play a very important role in an object-oriented language (OOP). They achieve reusability and testability. Functions define behavior. They are a block with the name. They are responsible to process some data or perform some calculation and return the result. Functions are called by the main program or objects. In idiomatic Kotlin function signature can look like below Figure 2.3:

Figure 2.3

A call to function can look like:

fun main() {

println(calculateStrikeRate(runs = 100, ballsFaced = 86))

}

Most of you will be fairly knowing the function already. And understanding the basics of the function should not require much explanation. In Kotlin a form of function can vary based on your requirements. We will cover each and every type of function call in this section. Functions can be:

With and without parameters

With and without return type

Function with named arguments

Functions with default arguments

Function as an expression

Variable arguments

Functions and parameters

It is very likely to happen while writing a program that you just want to perform a small piece of code multiple times and in multiple places. Sometimes this does not require any parameters. Kotlin allows you to write a function without any parameters. In its simplest form it can look like:

fun finish() {

// Destroy the Activity

}

To give you an example, Android Activity can have a call to finish() function which will destroy itself and will be removed from the back stack.

Functions and return type

Functions which that explicitly specify the return type has have to return the mentioned type. For example,e.g. calculateStrikeRate returns the Float type and return keyword inside the body in Figure 2.3 makes sure it returns the Float as a result.

The return type is optional in Kotlin. There can be a function that does not return anything. You might be familiar with the void in Java. Similarly, Kotlin has a Unit as a type. If the function does not return anything it takes Unit as a type automatically. And Unit does not need to define explicitly. The compiler does it for you. A function displayIngredients can be written in two ways:

// Function with explicitly specifying Unit as a type

fun displayIngredients(ingredients: List<String>):Unit {

}

// Function without explicitly specifying Unit as a type

fun displayIngredients(ingredients: List<String>) {

}

Named arguments

You always have to name the parameters in any programming language. But Kotlin allows you to bind the value of a parameter with its name while calling it. This is one of my favorite features and you will like it too.

When you call any function it's expected that you provide their value in the sequence they are defined. For example, in calculateStrikeRate function signature specifies that the first parameter is runs and the second parameter is ballsFaced. But both are of integer type. While making call if parameters are not passed in the correct order with their correct value, it can create blunder in your program and can corrupt your application:

fun main() {

val runs = 100

val ballsFaced = 86

// There is no compiler error, causes blunder

println(calculateStrikeRate(ballsFaced, runs)) //Results 86

// Kotlin will make your system safe

println(calculateStrikeRate(ballsFaced = ballsFaced, runs = runs)) //Results 116.27

}

Observe the previous code snippet. In last line sequence of parameters is changed, but values are bind against the correct parameter name using the parameter's name. This makes sure your function behaves correctly. There are few caveats with named arguments. Mixing of position-based arguments and named arguments are only allowed in certain conditions. Check below code:

// Allowed if position-based arguments are ahead of named arguments

println(calculateStrikeRate(runs, ballsFaced=ballsFaced))

// Named arguments ahead of position based arguments are not allowed and results into error

println(calculateStrikeRate(runs=runs, ballsFaced))

Sometimes playing named arguments can be cumbersome. Imagine your function is having more than 4-5 parameters. Imagine you have to rename any parameters. This means you have to update its named arguments everywhere it's used. But Android Studio which is built on top of IntelliJ IDEA is very helpful here. They have a shortcut to rename all references of parameter names along with named arguments. In Mac OS usually, Shift + F6 works as a shortcut. See Figure 2.4:

Figure 2.4

Android Studio also allows to easily name and un-named the arguments. On Mac OS Alt + Enter shows all possible options and shortcuts. See Figure 2.5:

Figure 2.5

Note: Named arguments don't work in Java API or Android SDK's API. This feels strange but there is a reason behind it. Kotlin aims to be compatible with Java 6 and above. Because below Java 8 storing parameter's name is not possible. That's the limitation of the .class file. So the compiler cannot recognize this and it will always throw an error.

Default arguments

Yet another variation in function parameter is default arguments. C++ was having this feature, but when it comes to Java they removed it. Java was having a very strong reason to remove this feature. Let's understand how arguments work default. You can provide a default value to any of the arguments, this means that argument becomes optional and can be skipped while making the call to functions:

fun greet(greeting: String = "Hello!", message: String) {

println("$greeting $message")

}

fun main() {

greet(message = "Hardik") // Prints Hello! Hardik

greet(greeting = "Good Morning!", message = "Hardik") //Prints, Good Morning! Hardik

}

In the above code snippet, greet function has greeting as a default argument and value for that argument is Hello. You can call this function in two ways which are shown inside the main function. Sensibly written default arguments can remove the need to function overloading.

Like named arguments, default arguments also have some caveats. Take a look at the below example:

fun printMultiplicationTable(number: Int, limit: Int = 10)

printMultiplicationTable(10)// Works fine as default argument comes last

The printMultiplicationTable object can be easily called just by passing some value and the value will automatically bound against parameter name number. But in the previous example of greet function, if you simply call it as below results into error. That means if a default argument is preceded a parameter without default arguments, then other parameters has to be called with named arguments:

greet("Hardik") // Without named parameter gives error.

Why Java does not support default arguments?

When the Java team decided to come up with a new language, they also aimed for simplicity so that more and more developers can adopt this new language. The default argument feature can cause a lot of confusion. You will be left puzzled the way it works. There can be ambiguity sometimes. Let's add a twist to greet function. Below example highlights the twist:

fun greet(greeting: String = "Hello!", message: String) {

println("$greeting $message")

}

fun greet(message: String) {

println(message)

}

If now somebody calls greet(message = "Hardik"), which function will be called? Java removed this kind of possibility by simply removing the feature itself. Sometimes KISS (Keep it simple, stupid) principle also matters.

Functions as an expression

Many times functions are much smaller in terms of the amount of code written as it's the body. For example, finding a maximum between two numbers, performing basic calculations like add, subtract, etc. In this case, the body of a function can be written as an expression. In the programming world expression is a combination of variables, constants, and operators. They are evaluated using the assignment (=) operator.

For such cases Kotlin allows you to omit the extra syntax and just focus on the body. You can skip writing curly parenthesis and return keyword. Check out some examples below:

fun findMax(num1: Int, num2: Int) = if (num1 > num2) num1 else num2

fun add(a: Int, b: Int, c: Int) = a + b + c

When functions are written as expression, Kotlin automatically infers the return type as well. But if functions are written normally inside curly parenthesis and returns the value, it must specify the return type.

Tip: This is a very useful feature to make your code concise. Your Android Studio or IntelliJ IDEA can remind you to make these changes by throwing lint warnings. Follow Figure 2.6 and select severity as a warning and such possible improvement will be highlighted by your IDE and you can change it right away.

Figure 2.6

Variable arguments

If the function accepts only the same type of arguments multiple times you can use variable arguments feature. Nowadays it's not so popular amongst developers. In such cases developer prefers to use Type like Array or List. But you might encounter such functions in any of the legacy code. I am covering this section to demonstrate something interesting.

Imagine there is a function in your legacy code which adds n number and returns a result. And you want to call that Java function from your Kotlin code:

// Legacy Java code

public class Util {

public static double sum(int… numbers) {

double ans = 0;

for (int number : numbers) {

ans += number;

}

return ans;

}

}

// Kotlin code accessing Java code

fun main() {

// With series of Int

println(Util.sum(50, 32, 43, 5454))

// With integer array

println(Util.sum(*intArrayOf(10, 20, 30)))

}

Focus on last line in above block println(Util.sum(*intArrayOf(10, 20, 30))). See how the argument is passed into the sum function. Don't worry the pointers are not back in Kotlin. It's called a spread operator. It breaks the array into the individual element.

Beyond arguments and return types

So far whatever we learned is typical variations of passing parameters and return types. What makes Kotlin more fun is some new concepts they have to write functions. Apart from these traditional features which many programming languages can have, Kotlin supports some out of the box features. And these features are beyond the standard tricks and tweaks in arguments and return types.

Kotlin supports the following:

Local function

Extension function

Infix function

Top-level function

Local function

A function within a function is called a local function. The local function can only be called from the function which has defined that. Any outside function or object is not allowed to call local function. In short,, the scope of local function remains within the parent function.

The local function can access the parameter of parent function without doing anything. It can also access local variables of outer/parent function.

Let's say we want to display the career graph for a cricket player. We could write a function that does the calculation. But ideally, cricket career graph contains multiple data like runs scored, balls faced, average, strike rate, the fifties and hundred scored. You can imagine a few more small functions are required to calculate such data.

Take advantage of local functions something like below code:

fun showCareer(runsScored: IntArray, ballsFaced: IntArray) {

val totalRuns = runsScored.sum()

val totalBallsFaced = ballsFaced.sum()

fun calculateAverage(): Float = totalRuns.toFloat() / runsScored.size

fun calculateStrikeRate(): Float = (totalRuns.toFloat() / totalBallsFaced) * 100

fun calculateFifties() = runsScored.filter { it in 50..99 }.count()

fun calculateCenturies() = runsScored.filter { it >= 100 }.count()

println("Total runs $totalRuns")

println("Total balls faced $totalBallsFaced")

println("Average ${calculateAverage()}")

println("Strike Rate : ${calculateStrikeRate()}")

println("Fifties scored: ${calculateFifties()}, Centuries made: ${calculateCenturies()}")

}

fun main() {

showCareer(

runsScored = intArrayOf(10, 55, 120, 67, 0),

ballsFaced = intArrayOf(5, 60, 70, 62, 10)

)

}

Local functions are great to achieve readability and encapsulation.

Extension function

Many times we face the situation where we need some extra ability to the existing class. The class may not be in our control. A standard OOP principal will advise to extend the class and add the new functionality which is required. This principal has a limitation if the class which you want to extend is final. Kotlin addresses both the problems with the help of the extension function.

Extension function allows to extend the functionality of a class without extending it. Assume you want to check whether the given string is a valid email or not. In an ideal Java world, this situation is a candidate for a utility function. But Kotlin developer will define an extension function like below:

fun String.isValidEmail(): Boolean {

return this.isNotEmpty() && this.matches("^[\\w-_\\.+]*[\\w-_\\.]\\@([\\w]+\\.)+[\\w]+[\\w]$".toRegex())

}

The string is a final class in Java and it can not be modified. But using extension function you can call isValidEmail from any String data. Take a look:

fun main() {

println("some_email@email.com".isValidEmail()) //Returns true

println("hello".isValidEmail()) //Returns False

}

The extension function are is resolved statically. By defining an extension function for any type, you basically create a static function under that class. Assume you define the above extension function inside a file called ExtensionFunction.kt and if you decompile the Kotlin generated class file, you get below code:

public final class ExtensionFunctionKt {

public static final boolean isValidEmail(@NotNull String $receiver) {

Intrinsics.checkParameterIsNotNull($receiver, "receiver$0");

CharSequence var1 = (CharSequence)$receiver;

boolean var10000;

if (var1.length() > 0) {

var1 = (CharSequence)$receiver;

String var2 = "^[\\w-_\\.+]*[\\w-_\\.]\\@([\\w]+\\.)+[\\w]+[\\w]$";

Regex var3 = new Regex(var2);

if (var3.matches(var1)) {

var10000 = true;

return var10000;

}

}

var10000 = false;

return var10000;

}

public static final void main() {

boolean var0 = isValidEmail("some_email@email.com");

System.out.println(var0);

var0 = isValidEmail("hello");

System.out.println(var0);

}

// $FF: synthetic method

public static void main(String[] var0) {

main();

}

}

Tip: How to decompile Kotlin generated bytecode into Java code? Select from menu, Tools/Kotlin/Show Kotlin Bytecode. A window will pop up displaying you a bytecode. Press on a decompile button and it will generate a Java equivalent file for you.

You may feel that under the hood extension function are doing some dirty hacks to achieve the syntactic sugar.

Similar to extension function you can also define extension property. Below extension property will return the last index for any type of list:

val <T> List<T>?.lastIndex

get() = if (this == null) throw IndexOutOfBoundsException("List is null") else this.size - 1

println(listOf(1,2,3,4,5).lastIndex)

Above code, snippets highlight two-part. A nullable receiver and providing getter for the property. An extension property or function can be called on the null object is defined with the nullable receiver. By doing so it's a developer's responsibility to provide the side effect when the receiver is null.

Infix function

Infix functions are one more way to provide get readability to your code. It looks like a natural language. The most popular infix function in Kotlin is to, this is used to create HashMap:

val map = hashMapOf(

1 to "Sunday",

2 to "Monday",

3 to "Tuesday",

4 to "Wednesday",

5 to "Thursday",

6 to "Friday",

7 to "Saturday"

)

The same infix function can be called as val pair = "Java".to("Kotlin"). Infix function can be called without using the dot operator and parenthesis.

To define your infix function, you need to use the infix keyword. Let's see how we can also write the same function. Basically to function returns an instance of Pair class. And hashMapOf function takes variable arguments of instances of Pair. Take look at below code snippet:

infix fun <K, V> K.mapTo(value: V) = Pair(this, value)

val months = hashMapOf(

1 mapTo "Jan",

2 mapTo "Feb",

3 mapTo "Mar",

4 mapTo "Apr",

5 mapTo "May",

6 mapTo "Jun",

…………..

)

While creating infix function you have to keep certain points in mind.

Infix function must be an extension function or a member function.

It can only and only have one parameter.

The parameter can not be the variable arguments.

The parameter can not have a default value.

Note: While calling infix function which is defined as a member function, you must provide both receiver and parameter. For example, if above-defined mapTo function is a member function it cannot be just called like mapTo "someValue"

Top-level functions

Functions in Kotlin are first-class citizens, this means they can be defined anywhere and they can be passed anywhere as a parameter. In a simpler word, top-level functions are defined outside of any class or interface. Any .kt file can hold multiple top-level functions. Extensions functions and constants are usually defined as top-level functions and properties respectively. Top-level functions save you to create an unwanted class file just to hold function. One can argue that with functions defined class file helps you to group functions. But in Kotlin you can define all related functions in single file and still keep them together:

fun add(a: Int, b: Int) = a + b

fun subtract(a: Int, b: Int) = a - b

fun division(a: Int, b: Int) = a / b

fun multiple(a: Int, b: Int) = a * b

The above code can be kept in a Calculator.kt file to represent them as a group.

Note: Top-level functions and properties by default get the package level visibility. That means if you want to access them outside its package, you need to import them by adding import statements.

Playing with String

In section Understanding basic types we learnt about String as a type. Java used to provide a lot of significant utility functions to deal with String type. This section focuses on what utility functions Kotlin provides to make String usage easy and efficient and concise.

Standard extension properties and functions

Before we start, checkout following code. It shows usage of few standard and very useful extension properties and extension function:

val book = "Android application development with Kotlin"

// Returns the last index of String and -1 is it's empty

println(book.lastIndex)

//Returns the range of indices starts with 0

println(book.indices)

/* Easiest way to get the subsequence. Given position is inclusive and throws

StringIndexOutOfBoundsException if any of the range value doesn't match */

println(book.subSequence(3..15))

Many times we want subsequent String before or after some specific value. Java was not having any easy way to do it. Here is how you can do it in Kotlin. The code below is in continuation of the String object book defined above:

/**

* Returns the sub string before the first occurrence of given character or String.

* Output : An

*/

println(book.substringBefore("d"))

/**

* Returns the sub string before the last occurrence of given character or String.

* Output : Android application

*/

println(book.substringBeforeLast("d"))

/**

* Returns the sub string after the first occurrence of given character or String.

* Output : roid application development with Kotlin

*/

println(book.substringAfter("d"))

/**

* Returns the sub string after the last occurrence of given character or String.

* Output : evelopment with Kotlin

*/

println(book.substringAfterLast("d"))

/**

* The input given to function as [delimiter] is case sensitive. It also has additional

* parameter [missingDelimiterValue] which returns the given String back as a result if the

* [delimiter] is not found within the String. [missingDelimiterValue] is a default

* arguments which defaults to original String if parameter value is not passed

*

* Output: Value not found

*/

println(book.substringBefore(delimiter = "D", missingDelimiterValue = "Value not found"))

Playing with regular expression is an easy peasy task. Checkout the below code:

//Convert any String to regular expression

println("^[\\w-_\\.+]*[\\w-_\\.]\\@([\\w]+\\.)+[\\w]+[\\w]$".toRegex())

//Easily matches your input against given regular expression in just one line

println("hello@email.com".matches("^[\\w-_\\.+]*[\\w-_\\.]\\@([\\w]+\\.)+[\\w]+[\\w]$".toRegex()))

Splitting the String is more fun now. Not just you can split them with the delimiter you want but you can limit your split as well. You can command to split String with a comma (,) and limit to a maximum of 5 objects after split:

/**

* Splits the give String using the delimiter and occurrences you have provided.

*/

println("A ,, string, ,, full, of, comma".split(",", limit = 5))

/**

* Allows you to specify multiple delimiters as well

*/

println("A ,, string. , . full, . of, comma and dots".split(delimiters = *arrayOf(",", "."), limit = 5))

There are plenty of such utility functions that Kotlin has provided. You can take an in-depth look here https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html

String template

Java used to provide an easy way to concatenate any of the String. They did operator overloading and use + operator to concatenate any of the String. Kotlin's team went one step further and finds out that the readability issue can be solved even in this tiny feature. They came up with a feature called String template or String interpolation.

With this feature, you can use any of the variables and even functions to concatenate their value in a string and still maintain the visibility.

fun main() {

val book = "Android application development with Kotlin"

val author = "Hardik Trivedi"

println("The book $book is authored by $author and it's published by ${getPublisherName()}")

}

private fun getPublisherName() = "BPB Publications"

In the above code, $ operator is used to interpolate the variable inside the String. One thing to note here is if you want to interpolate the output of a function you need to use ${}. It doesn't matter that function is a member function or a top-level function, you can interpolate any functions and any kind of properties as long as they are visible to the file where you want to use them.

String comparison

In any programming language, the string comparison is always an interesting topic to discuss. The various puzzles around the output of String comparison are also enjoyed a lot in the developer's community. With String comparison, one would like to compare either the value or reference. Let's take a look at each of these cases.

Comparing value

Kotlin uses == operator to compare the value or structure of a class. Do not ever mix it up with Java's == operator. In Java == operator is used to compare whether objects are the same or not. With == operator Java compares whether two objects share the same memory location or not. Let's see how code looks if we want to compare the value of two objects.

val book1 = "Android application development with Kotlin"

val book2 = "Android application development with Kotlin"

val book3 = "Kotlin Blueprints"

println(book1 == book2)// Returns true

println(book1 == book3)// Returns false

The above code is a very straight forward case which that does not require much explanation. But there is one thing to note here, in Kotlin String class can only be initialised initialized as if they are literals. So you don’'t have to worry about whether the value goes to the String pool or heap memory. All strings by default goes to the String pool.

But in the case of other types of classes situation could be different. Let's take one more example:

data class Recipe(val name: String, val ingredients: List<Ingredient>)

data class Ingredient(val ingredient: String)

val friedChips = Recipe(

name = "Fried Chips",

ingredients = listOf(

Ingredient("Potato"),

Ingredient("Oil"),

Ingredient("Salt"),

Ingredient("Pepper")

)

)

val frenchFries = Recipe(

name = "Fried Chips",

ingredients = listOf(

Ingredient("Potato"),

Ingredient("Oil"),

Ingredient("Salt"),

Ingredient("Pepper")

)

)

println(friedChips == frenchFries)// Returns true

Even if friedChips and frenchFries are two different objects of Recipe class, the value comparison returns true. This is happening because we have marked the Recipe and Ingredient class as a data class. And data class always focuses more on data/value. Here even if the objects are different they are the same as far as value is a concern. If you make either Recipe or Ingredient class, a normal class by removing the data keyword, you will see a different output:

println(friedChips == frenchFries)// Returns false after removing data keyword for Ingredient class

Comparing reference

Entirely new operator === (Triple equal) is used to compare references. This compares if two object shares the same memory location or not. === operator works exactly the way == operator used to work in Java.

In extension of above example try to perform below two checks and check the output:

println(book1 === book2)// Returns true

println(friedChips === frenchFries)// Returns false

For sure you will have a question that if both String and Recipe are a class why String reference comparison returns true and for Recipe it's false. Well, as explained before String is always a special class and all strings get memory in a shared String pool. The string is not a wrapper class so whenever values will be same all types of comparison returns true.

Similarly for other types like Int, Float, Double, Boolean both value checks and reference checks are the same, because they are always get initialized as if they are literals.

Using .equals() function

There is one more type of check, that is, fun equals(other: Any?): Boolean. It behaves exactly the same as == except some Float and Double data type comparison. This all starts if you go back in history where IEEE 754 was defining few of the principal like NaN, the Mentissa, values of infinity etc. IEEE 754 defined the standard for floating point representation. Representing floating numbers in both negative and positive values. And unlike other programming languages, Kotlin also respects these standards. And that is the reason of equal function behaving bit different when it comes to floating numbers. For example:

println(Float.NaN == Float.NaN) //Returns false

println(Float.NaN.equals(Float.NaN)) //Returns true

Both +0 and -0 are considered as distinct values:

println(0F == -0F) //Returns true

println(0F === -0F) //Returns true

println(0F.equals(-0F)) //Returns false

Tip: Always use .equals() function while comparing values for Float and Double data type. In every other case to compare values use == operator.

Escaped string

A slightly different type of String is escaped string. This is because Kotlin String can contain some special values to indicate a new line character, tab, backslash, etc. This can be represented in String using escape character. Kotlin supports below escaped characters:

\t: Inserts tab.

\b: Inserts backspace.

\n: Inserts newline.

\r: Inserts carriage return.

\': Inserts a single quote character.

\": Inserts double quote character.

\\: Inserts backslash.

\$: Inserts dollar character.

Try below code and observe the output:

println("Android Application Development with Kotlin\n")

println("A practical guide to learn Kotlin for Android\n\t\tAuthor by:Hardik Trivedi")

println("Price : \$10")

Controlling program flow

Kotlin is an object-oriented programming language. And they have functions. The nature of the function is to execute instructions from top to bottom. But executing top to bottom is not always an ideal scenario. There can be requirements to execute some other flow based on certain conditions. We call this branching in technical terms. Branching means your program has the choice to deviate from a standard flow based on criteria defined inside the system. Control flow can be a breakdown in three different way:

Decision making

Performing iteratively

Jumping off the branches

Decision making

At times in your program based on the state of one or more objects/variables, it decides to choose a different path of execution. This process is called design making.

If, else-if, else

The simplest example of the decision making process is performing if-else checks. Below is the example of if-else block in it's simplest form:

if (num.rem(2) == 0) {

println("Number is even")

} else {

println("Number is odd")

}

If takes an expression that can result in either true or false. When expression results into true if block is executed, otherwise it executes else block. Of course else block is an option like all other programming languages. Kotlin does not have a ternary operator. But using if else block you easily write such conditions. For example:

val maxNumber = if (num1 > num2) num1 else num2

Such if-else block does not require an explicit return keyword to return the result of an expression.

Note: The return keyword is mandatory if the function is not written as an expression.

You can also evaluate multiple conditions using if-else ladder:

fun evaluateGrade(marks: Int): String {

return if (marks >= 70) {

"Distinction"

} else if (marks >= 60) {

"First class"

} else if (marks >= 50) {

"Second class"

} else if (marks >= 30) {

"Pass class"

} else {

"Fail"

}

}

Opposite to Java, please note how to return statement is lifted out of each and every condition block. This allows you to focus more on the core logic. The return keyword is just a formality to satisfy the need of compiler, for us the logic of branching is more important.

‘when’ expression

Kotlin's team felt if-else ladder is bulky, to them, it was nothing but cases. So they came up with even better syntax which looks more natural and readable. In Kotlin you can use when expression to represent case by case scenarios.

Then when the expression is designed flexibly. It can function as both if-else ladder and the traditional switch case as well. And when expression eliminates the issues which both of the traditional control flow was having.

The concept is simple it evaluates arguments against all the branches until the condition is satisfied.

The above evaluateGrade function can be written using when expression:

fun evaluateSmartGrade(marks: Int): String {

return when {

marks >= 70 -> "Distinction"

marks >= 60 -> "First class"

marks >= 50 -> "Second class"

marks >= 30 -> "Pass class"

else -> "Fail"

}

}

As always IntelliJ IDEA will always help you to improve your code and make it more optimized. Alt + Enter will show you the option to convert your if-else block to convert into when expression. See Figure 2.7:

Figure 2.7

‘when’ as switch case

While writing the switch case in Java the most important thing was writing the break statement. If you forgot to specify the break statement you may end up in erroneous output. Kotlin eliminates this possibility by removing the break statement from the syntax. The moment the case meets the condition the flow is terminated. There is no fall through for when expression. You can combine multiple cases and point them to perform the same task is the condition is satisfied.

Imagine an examination system, which generates remarks based on the grade of a student. The function can look like below:

fun getGradeRemarks(grade: String) = when (grade) {

"A" -> "Excellent!"

"B" -> "Good!"

"C" -> "You need to work little bit more!"

"D" -> "You need to do real hard work!"

else -> "Invalid grade. Are you from Mars!"

}

Here the else block serves as a default case in the traditional switch case.

Note: Using ‘when’ as an expression where it directly returns the output as shown above writing else block is mandatory. Here compiler needs something to written if the grade is not matching with any of the cases. By forcing developers to write else case Kotlin promotes safe coding practice.

You can also evaluate the function as a subject for when expression, and there is one more feature added from Kotlin 1.3 onwards where the result of the function can be hold as a subject and can later on used inside when expression for better understanding of code. Take a look at the below code where the current month is captured as month variable and used inside when expression:

fun getCurrentMonthName(): String {

return when (val month = Calendar.getInstance().get(Calendar.MONTH)) {

0 -> "Jan"

1 -> "Feb"

2 -> "Mar"

3 -> "Apr"

4 -> "May"

5 -> "Jun"

6 -> "Jul"

7 -> "Aug"

8 -> "Sep"

9 -> "Oct"

10 -> "Nov"

11 -> "Dec"

else -> "Invalid number $month"

}

}

‘when’ with objects

Unlike Java where switch case was only possible using constants like String, enum, and Integer when expression in Kotlin is way powerful. You can use any object to compare them case by case:

fun whatIsThisType(temperature: Any) {

when (temperature) {

is Short, Int, Float, Double -> println("I can perform mathematical operations")

is String -> println("I have series of characters")

is Exception -> println("I am an exception, something is wrong")

is List<*> -> println("I have collection of objects")

is Map<*, *> -> println("I am holding some key value")

}

}

You can form such expressions with any kind of arbitrary type.

Perform iteratively

You will end up in a lot of such scenarios where you want to iterate over a list of data and bind it to some other type and showing up on the user interface. How do you do it? Of course, using loops. Kotlin mainly offers for and while loop. Kotlin has many types who offer iterable. And anything which is iterable can be used for forming loops.

Using for loop

There is only one variant of for loop. The for loop looks like for each loop of Java. It has no verbosity at all. By default it increments 1 step and in ascending order. Below code iterates from 1 to 10:

for (count in 1..10) {

println(count)

}

If you want to perform a loop in reverse direction you can do it too.

for (count in 100 downTo 1) {

println(count)

}

Note: If you use range in loop e.g. 1..100, you can only form range in ascending order. You cannot have range e.g. 100..50, you have to use downTo infix function in this situation. You can also use it until the infix function to indicate the incrementing loop.

You would have a lot of many questions and possible variants coming to your mind.

Below are some more example of forming a for loop with different type of data, for example, Array and List:

val intArray = intArrayOf(30, 23, 34, 652, 575, 23, 668, 237, 9893)

/*Iterating over array*/

for (item in intArray) {

println(item)

}

/*Iterating over array with index, using destructuring declaration*/

for ((index, value) in intArray.withIndex()) {

println("$value at index $index")

}

val autoBotList = listOf("Optimus prime", "Bumble bee", "Ratchet", "Iron hide", "Jazz")

/*Iterating over list*/

for (autoBot in autoBotList) {

println(autoBot)

}

/*Iterating over list with index, using destructuring declaration*/

for ((index, value) in autoBotList.withIndex()) {

println("$value at index $index")

}

/**

* Iterating over list with index, in reverse order

*/

for ((index, value) in autoBotList.asReversed().withIndex()) {

println("$value at index $index")

}

The great while loop

The for loop is a very simple approach. What if you don't want just simple increment and decrement as progression value. What if you want to do some complex calculations as criteria to exit the loop? The while loop is just perfect for this. Here as well there is no major change compared to Java. Both while and do-while loop work as they used to work in Java.

The while loop first checks the condition and then executes the body.

On the other hand, the body of do-while loop is executed at least once and before doing the second iteration the condition in check.

How these two flavors of while loops can impact your system are shown by funny graphics as Figure 2.8:

Figure 2.8

The only little modification in do-while loop is you can define the variable inside the body of a loop and remains accessible outside the scope of do block, which can be later on used as a while expression.

// Simple while loop

var num = 1

while (num <= 10) {

println(num)

num--

}

// Do while loop

do {

val data = getData()

println(data)

} while (data != null)

Jumping off the branches

While decision making or doing iterative operations you may want to terminate/reset the process and collect the result back at that in whatever state it is.

Kotlin has three such statements which provide structural jumps:

return: By default returns from the nearest enclosing function or anonymous function.

break: The nearest enclosing loop gets terminated.

continue: Skips to the next iteration to the closest loop.

A return statement may or may not return the result. But it makes sure execution is terminated and control goes to it's nearest caller function:

fun addPositive(num1: Int, num2: Int) {

if (num1 < 0 || num2 < 0)

return

println(num1 + num2)

}

Both break and continue expression are used to terminate the loop, but there is a difference between them. To understand the difference run the below code and observe the output:

/**

* Terminates the loop completely and jumps to the given label

*/

brk@ for (i in 1..5) {

for (j in 1..5) {

if (i == 2 && j == 2) {

break@brk

}

println("i=$i and j=$j")

}

}

/**

* Skips just an iteration and jumps to the given label

*/

cntnu@ for (i in 1..5) {

for (j in 1..5) {

if (i == 2 && j == 2) {

continue@cntnu

}

println("i=$i and j=$j")

}

}

println("Out of all loops")

Handling errors and exceptions

No software or program will end up executing successfully every time. Good software can identify the erroneous input or fault in the software caused by input, and it can also recover from that state. On the other hand, not all the software failures are caused by faulty input, sometimes it's the environment. The software relies on the hardware configuration of a computer on which it runs. Hardware failure is not in control. So these type of failures is which is non-recoverable are known as errors. And recoverable failures are known as exceptions.

All the programming languages that I know of throws different Error and Exception. They contain the message, a complete stack trace and reason which could be optional. A software developer should try to anticipate all possible scenarios and make software robust. Kotlin offers a try-catch block to handle such errors and exceptions. A typical try-catch block can look like below:

fun main() {

try {

println(isEligibleToVote(9))

} catch (e: Exception) {

println(e.localizedMessage)

}

}

Whatever you write inside try block is expected to throw an exception. Whenever an exception is thrown it terminates the execution of try block from that point and it executes the catch block. It's best practice to catch all possible exceptions and handle them gracefully. You can show a message to the user and start the journey again, or you can log the error message and move ahead.

Kotlin also supports finally block. A complete try expression can look like below:

try {

// some code

}

catch (e: SomeException) {

// handler

}

finally {

// optional finally block

}

The rule is simple. The try block must be followed by either catch or finally block. There can be multiple catch blocks. All rule which Java has is followed in Kotlin as well.

There is only one difference. In Kotlin try-catch is an expression. This means it can return a value. Let's try to write a function to read a file. (Please note, below code is just to explain try as an expression with the return value. In reality, Kotlin offers a much better way to read the file, we will learn that later in the same chapter.):

fun readFile(fileName: String): String? {

return try {

val fileReader = FileReader(fileName)

val bufferedReader = BufferedReader(fileReader)

var line: String? = null

val string = StringBuilder()

while ({ line = bufferedReader.readLine(); line }() != null) {

string.append(line)

}

bufferedReader.close()

string.toString()

} catch (ex: FileNotFoundException) {

null

} catch (ex: IOException) {

null

}

}

In the above code snippet, you can see how we have lifted out the return function to make sure both try and catch blocks return something and by default the last line of try and catch block becomes the value to be returned.

Checked exception

Java exception API is categorized in Exception and RuntimeException. One which is a direct subclass of Exception is called checked exception and others which are a subclass of RuntimeException are called an unchecked exception. A compiler error is thrown if a checked exception is not handled. If your program is calling any such piece of code that throws checked exception then this has to e written inside a try-catch block.

On the other side, there are languages like C++, Scala, Objective C, and even Kotlin does not support checked exceptions. The problem with the checked exception was many times we don't know what to do with that exception. The exception is thrown from some deep down function where we never anticipated an error. For example, let's say we want to display recipes with all details like content, it's photos, etc. You may break down functions like:

public Recipe getRecipe(int id) throws IOException {

getRecipeMakingSteps(id);

getRecipeIngredients(id);

getRecipePhoto(id);

}

public List<String> getRecipeMakingSteps(int id) {

// Your code

}

public List<Ingredient> getRecipeIngredients(int id) {

// Your code

}

public File getRecipePhoto(int id) throws IOException {

// Your code

}

In the above function, we chose to re-throw the exception. But this will make a long chain of the thrown exception because whichever function now calls the getRecipe function has to handle it. And caller function will have no idea why this exception is thrown and how to handle it. For that caller function, this is not recoverable, so maybe it will leave as it is. May be developer will just log the exception, or swallow it.

Also imagine a situation where at some point in time, getRecipePhoto decides to throw some other kind of checked exception. All the caller function has to handle now one more checked exception. Scalability and handling issues make checked exception evil.

There is also a very fundamental issue with checked exception is readability. Consider below case:

List<Recipe> recipes = // Your code

List<Recipe> data = recipes.stream().map(recipe -> {

try {

return getRecipePhoto(recipe.id)

} catch (IOException e) {

// Here is not the correct place to do the handling

}

})

.collect(Collectors.toList());

It looks odd, it puts you in a miserable position, and it's not readable. The widely used solution is whenever a checked exception is thrown, the nearest caller function will catch it and re-throw unchecked exception with a relevant message:

public Recipe getRecipeSafely(int id) {

getRecipeMakingSteps(id);

getRecipeIngredients(id);

try {

getRecipePhoto(id);

} catch (IOException e) {

throw new RuntimeException("Error while fetching recipes");

}

}

By doing this you will be in a better position and code better and faster without worrying about handling the exception at all levels. So Kotlin compiler does not force you to catch the exception and provide the handling for it. You can skip this step. But you can always handle the exception whenever you wish.

Note: From the Java compatibility angle, even if your Java code is throwing a checked exception, Kotlin will not force you to handle the exception.

Playing around functions

In the very same chapter, we learned about function under section Basics. We now know arguments, return types, various types of function. Kotlin has a lot more to offer in function. Do you know there is already some great collection of extension functions available in standard Kotlin API? Do you know you can execute a block of code within the scope/context of some object? Do you know you can return multiple values from a function? This section is dedicated to finding out answers to all these questions.

Useful extension function

Extension function can easily get rid of your utility classes. This provides an easy way to apply function as if it's straight forward provided from the class itself. There can be an endless possibility of the extension function. There are already good enough extension functions bundled into Kotlin's standard API. Let's take a look at them. We will compare with the Java code to understand how useful and concise the code can be.

Managing files

Managing files are a troublesome task in Java. Writing I/O related code means full of exception handling and ceremonies like opening and closing file streams. Kotlin has created extension functions to make this task easy. Below two functions are an example of how file handling can be an easy and fun piece of code:

//Writes the file with giving text

File(fileName).writeText("This is sample text to demonstrate file handling")

//Read the entire file returns the data as complete text as a String

val text = File(fileName).readText()

println(text)

The writeText and readText functions write and read the data byte into the file. There is also an extension function available to handle file using input streams. Using input streams and BufferedReader file handling is much efficient and faster compared to normal FileWriter and FileReader operations. Take a look at the below code which demonstrate the usage of input stream extension function:

//Read the entire file using buffered reader returns the data as complete text as a String

val textFromStream = File(fileName)

.inputStream()

.bufferedReader()

.use { it.readText() }

Scoping functions

You don't write file handling code daily, so apart from file handling extension function, there is the standard set of scoping functions that you can use in your day to day code. They are known as scoping functions because they let you handle the object under that scope very easily. They look very similar but they are fine-tuned to perform a very specific task. Imagine the different types of screw heads. To screw and unscrew them you need a different type of screwdrivers. Same is the analogy for Kotlin's scoping function:

Figure 2.9

Set if this scoping function can be listed as run, with, let, apply and also. This functions can be categorized in 3 ways:

Extension function and normal function.

it vs this as an argument

What value it returns

The function can be a member of multiple categories as well. For example,e.g. Aa function can be an extension function and can take either this or its type of argument and also may or may not return value. Let'’s see them case by case.

Using ‘with’ and ‘run’ functions

Imagine I want to read or write properties or call a function on a particular object in a sequential manner. Let's say I want to read multiple values from Person class:

with(person) {

println(age)

println(name)

println(address)

printIntroduction()

name = "Hardik Trivedi"

printIntroduction()

}

Function with calls the specified function which is passed as a parameter and returns the value if any. In the above code, the scope is automatically defined for object person and Kotlin explicitly operates on a person object. Within the with function, you don't need to explicitly use object name at all. This is useful until the person object is non-nullable. What if the person object is null? The function with does not work with null parameters. It only accepts non-nullable type as an argument with is a normal function.

Here comes run function to save you. It is an extension function. Using a run function you can modify the code which will perfectly work for you:

the person?.run {

println(age)

println(name)

println(address)

printIntroduction()

name = "Hardik Trivedi"

printIntroduction()

}

The ? operator is called a null safety operator which allows you to safely use nullable types. We will learn more about it in the next chapter. Function run works for both nullable and non-nullable types. It also returns the value for the function which is passed as an argument.

Note: Between run and with, in most of the cases run function will be the winner. Because run allows you to deal with nullable objects. This is an advantage over with function.

Using ‘apply’ and ‘also’ function

We saw both run and with returns the value which is specified as a block. What if we need same updated objects? For example, create an intent with multiple extras and start an Activity. Below code will show you the comparison between Java code and Kotlin code:

// A usual java code to start activity with intent having extras

Intent intent = new Intent(this, AnotherActivity.class);

intent.putExtra("NAME", <name>);

intent.putExtra("AGE", <age>);

intent.putExtra("ADDRESS", <address>);

startActivity(intent);

// Start activity with intent having extras using Kotlin

val intent =Intent(this@CurrentActivity, AnotherActivity::class.java).apply{

putExtra("NAME", <name>)

putExtra("AGE", <age>)

putExtra("ADDRESS", <address>)

}

startActivity(intent);

The apply function calls the specified block with this (its instance) value as its receiver and returns this (its updated instance) value. In the above code snippets, we are receiving an updated intent object which is filled with all extras. The code looks very much readable. The signature of the apply function looks like:

public inline fun <T> T.apply(block: T.() -> Unit): T

There is an another function called also where developers get confused a lot. They also function returns the same updated objects just like apply function. Check out its signature below and compare it with apply function's signature:

public inline fun <T> T.also(block: (T) -> Unit): T

Both also and apply are extension function and returns the updated object. There is only one difference, which is how its specified function is treated. Let's take a simple example and we will have a clear understanding of both the function.

Assume there is a class Number which has only one property num. And there is a function inc() which increments that num property by exact one if called. Now we will call inc() function three times using apply and also scoping function. Both will produce the same output. But the way we call inc() function is different for apply and also:

//Increment number using the apply function

fun numberIncrementerApply() {

val incrementedNumber = Number(10).apply {

inc()

inc()

inc()

}

incrementedNumber.printNumber()

}

//Increment number using also function

fun numberIncrementerAlso() {

val incrementedNumber = Number(10).also {

it.inc()

it.inc()

it.inc()

}

incrementedNumber.printNumber()

}

The key difference in the above two functions is apply function treats this value as it's the receiver and also function treats this value as it's the argument. That's why also function has to explicitly use it to refer itself when apply function get's everything implicitly. On top of this for both these functions, you also get the advantage of null safety. You can call these functions using a safe call operator (?).

Using ‘let’ function

This particular function is widely used in Kotlin. This provides a great way to safely handle nullable objects. The let function calls the specified block with this as its argument and returns the result if any. Of course like any other scoping function a typical definition is very confusing. Lets try understanding the below code:

// Using Java

private void letExample() {

Person person = null;

if (person != null) {

System.out.println(person.getAge() * 2);

}

person = new Person("Maanya", 5, "London");

if (person != null) {

System.out.println(person.getAge() * 2);

}

}

// Using Kotlin

fun letExample() {

var person: Person? = null

var doubleAge = person?.let { it.age * 2 }

println(doubleAge)

person = Person(name = "Maanya", age = 5, address = "London")

doubleAge = person?.let { it.age * 2 }

println(doubleAge)

}

The let function can also be used without a safe call operator. But let function on non-nullable function is not recommended. Because it does not give any benefit. In this case, using the run function would make more sense.

Note: Any standard scoping function which is an extension function can be used with a safe call operator. A safe call operator will only execute the code if an only if the object is not null. When the object is null and execution is requested, it simply skips the execution. These functions are generic. They can work with any type.

Below is the compiled flow chart, which will serve you as a cheat sheet and will help you to decide when to use which function:

Figure 2.10

Returning multiple values from the function

A few programming languages provide a way to return multiple values from a function. For example, Python and Go are the languages where a clear way is defined to return the multiple values. Kotlin does not provide a very straight forward way to deal with such a situation. But there are two specific classes which can solve the purpose. Kotlin provides Pair and Triple. These classes are not designed for any specific purpose. They can be used anywhere and in anyways. They are designed generic so they work with any data types. Any function can return the object of Pair or Triple type and those values can be utilized outside the function call.

Pair<A, B>

Consider and a function that wants to return two values. We can use Pair class here to return two values. Take a good look.

fun main() {

val career = showCareer(

runsScored = intArrayOf(10, 55, 120, 67, 0),

ballsFaced = intArrayOf(5, 60, 70, 62, 10)

)

println(" Total 50/100 : ${career.first}/${career.second}")

}

fun showCareer(runsScored: IntArray, ballsFaced: IntArray): Pair<Int, Int> {

val fifties = runsScored.filter { it in 50..99 }.count()

val centuries = runsScored.filter { it >= 100 }.count()

return Pair(first = fifties, second = centuries)

}

The showCareer function is returning fifties as the first argument of Pair class and centuries as the second. The main function calls showCareer which returns the Pair. We have captured the returned Pair instance and extracting values using properties named first and second.

You can argue about making class to return such data. But it is possible that the values which you want to return may not be necessarily related to each other. Also, the question is does it makes sense to create a class for such a small need? What if there are multiple such requirements? How many such random classes should be created? In such a situation we should use Pair or Triple class.

Triple<A, B, C>

Pair is used to return two values from a function. Similarly Triple is used to return three values. There is no difference in the way Triple works. It's ditto in every aspect with Pair, except it supports one extra value. Triple has properties like first, second, third to access all three objects:

val triple = Triple(first = "Hardik", second = 32, third = listOf("Ahmedabad", "Pune", "London"))

println("${triple.first} ${triple.second} ${triple.third}")

Both of these classes supports an extension function called toList() which converts Pair or Triple into List. Checkout below code:

/**

* Extension function Pair<T, T>.toList(): List<T>, prints List of centuries [2, 1]

*/

println("List of centuries ${career.toList()}")

/**

* Extension function Triple<T, T, T>.toList(): List<T>, prints List triple data [Hardik, 32, [Ahmedabad, Pune, London]]

*/

println("List triple data ${triple.toList()}")

Two Pair or Triple is equal if values within these two objects are equal.

Destructuring declaration

Many times we just wrap multiple objects inside class or List. This wrapped data is passed as a parameter or we catch it from some function call as a result. But we always unwrap them at some point in time. After unwrapping it we use those individual objects and process them further if required. For example, in the above section where Pair and Triple are explained, we used those classes just for the convenience of returning from a function. In reality, we wanted to use those values defined as first, second, third outside the function call. Destructuring a declaration can help us in such a situation. Let's modify the code a bit and we will decompose the Pair object and catch the result into separate properties named the fifties and centuries:

val (fifties, centuries) = showCareer(

runsScored = intArrayOf(10, 55, 120, 67, 0),

ballsFaced = intArrayOf(5, 60, 70, 62, 10)

)

println(" Total 50/100 : $fifties/$centuries")

Not only Pair and Triple classes support such features. Any class can be decomposed. Let's take one more example:

fun main() {

//Destructuring Recipe object which consist String and List

val (name, ingredients) = getRoastPotatoesRecipe()

println("To cook $name you need $ingredients as ingredients")

}

fun getRoastPotatoesRecipe(): Recipe {

return Recipe(

"Roast Potatoes",

listOf(

Ingredient("Potatoes"), Ingredient("Black Pepper"),

Ingredient("Olive oil"), Ingredient("Salt")

)

)

}

One more great example of a destructuring declaration could be iterating over the map. For example, assume there is a function that returns the days of the week as a Map and caller function wants to iterate over it.days of week as a Map and caller function wants to iterate over it. We can do it using a destructuring declaration. Check out below example:.

fun main() {

// Iterating over map using for loop and destructuring declaration

for ((dayOfWeek, nameOfDay) in getDaysOfWeek()) {

println("$dayOfWeek $nameOfDay")

}

}

fun getDaysOfWeek(): Map<Int, String> {

return mapOf( 1 to "Sunday", 2 to "Monday", 3 to "Tuesday", 4 to "Wednesday",

5 to "Thursday", 6 to "Friday", 7 to "Saturday"

)

}

With the destructuring declaration, you may have a question that what if the objects are decomposed and some of the property you do not want to use. Kotlin provides an optimized way to only decompose the property you want to use. While destructuring an objects Kotlin relies on componentN() function. If object has 5 properties internally there will be component1(), omponent2()…componentN() functions. For each destructured property Kotlin calls its relevant componentN() function to fetch the value.

Assume you are fetching a result from an API call that can be destructured into responseCode and actual response. And you don't want to use responseCode for some reason. You can use underscore (_) rather than naming a property while destructuring it.

val (_, result) = getResult()

Kotlin will skip component1() function call internally and this will make your code more efficient. In summary, use underscore to not to de structure unwanted properties from an object.

Being late or lazy

Late or lazy none of them is considered a good virtue in life. But in Kotlin being late or lazy can be awarded.

lateinit

Often a situation can arise where object or property is non-nullable but that needs to be initialized sometime later during execution of the program. For example, the dependency injection framework initialized objects later while compiling the code. The setUp() function of the jUnit test framework also initialize values before executing any test. In such a situation, you are very sure that before you access object or property they will be initialized and cannot be null. It just you don't want to initialize them now.

With lateinit you can indicate Kotlin compiler that you want an object or a property non-nullable and initialization will take place later on. Take a look at the below test class:

public class PresenterTest {

lateinit var presenter: SomePresenter

@SetUp

fun setup() {

presenter = SomePresenter()

}

@Test

fun test() {

subject.method()

}

}

The presenter is declared as lateinit and setup function will make sure that the presenter will get initialized before test() function runs. The lateinit modifier can only be used for your local class level properties. The lateinit modifier cannot be used in the below circumstances:

lateinit cannot be used for a type that is inside the primary constructor.

If the property has custom getter and setter lateinit cannot be used.

You cannot use lateinit if your property is any of the primitive types.

lateinit modifier is not allowed to use with property declared as val and can only be used with var modifier.

What happens when the property is marked as lateinit, and before its first use it's not initialized? This is sometimes likely to happen if the code is not structured well. In this scenario, Kotlin throws an exception which will say you that property is not initialized and it must be initialized before its first use.

To check a lateinit property is initialized or not, you can use initialized property:

lateinit var recipe: Recipe

fun main() {

if (::recipe.isInitialized) {

println("To cook ${recipe.name} you need ${recipe.ingredients} as ingredients")

}

}

lazy

Many times we want to initialize property at the class level because we want the property to use throughout the class. Such properties are initialized the moment the class is initialized. But such eager initialization is used much later in the class. How do you optimize such instruction? You can use lazy function. A lazy function executes a block and returns the value. This process only happens once. And if function calls occur multiple times it returns the old value and does not execute it again. Let's do a simple test.

val sayHello by lazy {

println("Inside lazy")

"Hello"

}

println(sayHello)

println(sayHello)

On executing the above code it prints both Inside lazy and Hello for first println statement, but for next it will only print Hello. With lazy property, the initialization will happen lazily and only once and remembered until the class gets destroyed and after that remembered result is returned.

One more interesting thing about lazy function is it's a thread-safe call. The value is computed in one thread and all other threads can see some value. A property that is lazily initialized has to be declared as val.

Tip: lateinit will always be used with modifier var and lazy can only be used with modifier val.

Type aliases

Type alias is used to provide an alternate name to any existing type. Why would you provide an alternate name? You can use type alias in the below circumstances:

If the name of the existing type is too long.

If it conflicts with any existing type name and you don't want to use a full package declaration.

If the existing name, you don't like it.

Map<String, String> can be renamed to StringMap. And every time you don't need to specify the type of key and value:

typealias StringMap = Map<String, String>

fun getProducts(): StringMap {

return mapOf(

"Avocado" to "£2",

"Whole meal bread" to "£1.60",

"Milk" to "£1.50",

"Yogurt" to "£1.90"

)

}

The defined typealias can be used anywhere in the project. The new typealias needs to be imported to resolve if used outside the package. See some samples below which demonstrates the usage of typealias in various scenarios:

// Refer ArrayIndexOutOfBoundsException as ArrayIndexException anywhere in project

typealias ArrayIndexException = ArrayIndexOutOfBoundsException

/* Suppose you have your Date class and want to use Java date class within same class/function

You can give type alias as JavaData */

typealias JavaDate = java.util.Date

// You can also type alias for function

typealias BooleanPredicate<T> = (T) -> Boolean

Type casting

While writing code many times we run into a situation where we need to check a type before operating over that object. Assume there is a function that does error handling based on the exception object is received. Further in this function we check each exception about its type and perform a specific action based on what type of exception it is. Think of a code something like below code snippet:

fun handleError(e: Exception) {

when (e) {

is NetworkUnavailableException -> println(e.message)/*Handle unavailable*/

is MalformedUrlException -> println(e.message)/*Handle malformed url*/

is UnauthorisedRequestException -> println(e.message)/*Handle invalid request*/

is InvalidResponseException -> println(e.message)/*Handle invalid response*/

else -> println(e.message) /*Handle generic*/

}

}

You may have noticed that Kotlin provides is the operator to check the type of any object at runtime. The is operator also supports negation. For example, !is will return true if the type does not match with the expected type. Below code snippet demonstrates how !is can be used:

fun isNotANumber(num: Any): Boolean {

return num !is Byte || num !is Short || num !is Int || num !is Long || num !is Float || num !is Double

}

Both are and !is works with other conditional operators. They just return Boolean and we are independent to use them the way we want. It's just like Java's instanceof. What added benefit is the operator gives you compare to Java? Time to find out.

Smart casting

The is operator has an added advantage that you do not need to perform explicit cast again to use them. Let's try to understand well by comparing the Java and Kotlin code:

// Java Code

public void performTypeCast(Object data) {

if (data instanceof String) {

System.out.println("It's a string and length is " + ((String) data).length());

} else if (data instanceof Number) {

System.out.println("It's a number and some calculation can be performed");

} else if (data instanceof Exception) {

System.out.println("It's an exception and message is " + ((Exception) data).getMessage());

} else if (data instanceof Boolean) {

System.out.println("It's a boolean with value " + data);

} else {

System.out.println("I don't recognise this type at the moment");

}

}

// Kotlin code

fun smartCast(data: Any) {

when (data) {

is String -> println("It's a string and length is ${data.length}")

is Number -> println("It's a number and some calculation can be performed")

is Exception -> println("It's an exception and message is ${data.message}")

is Boolean -> println("It's a boolean with value $data")

else -> println("I don't recognise this type at the moment")

}

An example from Java every time we have to perform explicit casting to get length from String or error message from Exception, but in Kotlin I get it straight away. Thanks to Kotlin’'s smart cast feature. This saves a lot of time and removes the verbosity. Clearly Kotlin is winning the heart here again.

If the compiler cannot assure that the object cannot change between the check and the usage, the smart cast will not work. What does this mean? Take a look at the below code:

var obj: Any? = null

fun main() {

obj = "Hardik"

if (obj is Number) {

obj = 30

// println(obj) Gives compile time error

}

}

The code is pretty much simple. There is a top-level declaration with modifier var. This means value can be changed anywhere and anytime. Now inside the main function after casting the property into a Number type, the property is being changed by the program. Kotlin will raise an objection because it immediately recognizes that property is changed after casting and this can lead to error. Properties with modifier var, the smart cast is not allowed because this can be changed anytime. If it's a local variable with var modifier than the rule is: it must not be modified between the check and usage.

Safe casting

The is operator returns boolean. It checks whether the objects are of a given type or not. What if you want to explicitly cast it? For explicit casting Kotlin provides as operator to cast data to your desired type. In below example you can cast data object to String and access the length property of it:

println((data as String).length)

Java equivalent code for above code could be:

String strData = (String) data;

System.out.println(strData);

On an unsuccessful casting, both Kotlin and Java can throw ClassCastException. To handle this exception you need to reply in a try-catch block and this makes code difficult to read. Kotlin provides another way to deal with this situation. They introduce a safe cast operator.

Safe cast operator (as?) will return null if casting to any given type was unsuccessful. The safest way to written casting in Kotlin is

fun main() {

cast(10)// Prints null

cast("Hardik") // Prints 6

}

fun cast(data: Any) {

println((data as? String)?.length)

}

Since the safe cast operator can return null, it will force you to use the safe call operator (?). And length property will only be called if the casting is successful.

One other way to avoid ClassCastException is to use generics. Both Kotlin and Java provide type safety while using generics. But during runtime, the generated instance does not hold any type of information. Kotlin provides a way to perform cast for generic type as well. Take a look at below code:

when (data) {

is HashMap<*, *> -> println(data[1])

is List<*> -> println(data[0])

else -> println("Given type is neither a Map nor a List")

}

We use * symbol to indicate that you know nothing about the type. This is called star projection in Kotlin.

Classes

For any programming language classes are very crucial parts. You can't imagine an object-oriented programming language without class. They are the essence of the language. Everything is around classes. Kotlin is also an OOP language. Classes are called blueprints or templates in the OOP world. They represent a real-life entity in a template form. In Java, a class is made of member data and methods. That's the typical structure of the class. Both member data and method have visibility modifiers. Visibility modifiers help to achieve encapsulation. In Kotlin the fundamentals are the same. For any class, Kotlin can have properties (member data) and functions (methods). In this section we will learn below topics:

Creating a class

Properties

Types of constructor

Classes, interfaces, and inheritance

Visibility modifiers

Data classes

Nested and inner classes

Enum classes

Sealed classes

Creating a class

To create a class you can simply use class keyword followed by its name. A class in Kotlin has a header and body. The header consists of its name, primary constructor and the name of classes and interfaces it extends or implements. The class body is surrounded by curly braces and it comes right after the class header. The class name should be defined in the upper camel case. This is the naming convention. Below is a sample Kotlin class:

Figure 2.11.png

While creating class, the only mandatory steps are to define its name with the class keyword. The class can be created without a constructor and body. A class without a body is an empty class. This class can also be important sometimes. For example, creating any custom exception, creating your type. Class is not just about creating an object. From the programming perspective, it is also a type. The more we define granular type the more it is useful:

class SomethingWentWrongException : Exception()

Above is valid syntax in Kotlin. You can give a more meaningful name to the exception class to make it more readable and meaningful. I would recommend an empty class only if they are marked as abstract because there is no meaning creating the object of an empty class.

Properties

Properties are nothing but member data or field in Java. A property in Kotlin holds the data inside a class. Property can be mutable (var) or immutable (val). Property is usually referred by its name only while accessing:

val hatchBack = HatchBack(numberOfDoors = 2, colour = "Red")

println("Doors ${hatchBack.numberOfDoors}")

println("Speed ${hatchBack.speedLimit}")

println("Colour ${hatchBack.colour}")

Your properties are public by default until you specified any visibility modifiers. You don't define getters and setters for each and every property which does nothing. Also, your properties are by default final. This means it cannot be overridden. Well, you might be thinking that this sounds strange. Property is public with no getter and setters, also you can't override it. Isn't it against the encapsulation principle? Let me tell you it's not. Private property with only a wrapper of getter and setter is as good as public property.

Kotlin recommends getters and setters only when you want to do something additional before reading or writing into value. Assume in a Car class there is a property that returns a Boolean for property isPremiumCar. This requires some additional computation. We can have a getter for such property. Below code snippet shows how you can define a getter for any property:

val isPremiumCar: Boolean

get() = speedLimit > 100 && (driveType == CarType.AUTOMATIC || driveType == CarType.SEMI_AUTOMATIC)

println(hatchBack.isPremiumCar)// Prints true or false based on getter

//Gives you compilation error because property is defined as val

hatchBack.isPremiumCar=false

The isPremiumCar is defined as val and has only get() as a function. This kind of property cannot be modified. If we attempt to set some value for this property it will give you the compile-time error. For setter property assume you want to set a name for the car. And the rule to set a car name is that it cannot contain any special character as a name. If the given string contains any special character an Exception will be thrown. You could easily use setter functions for this type of requirement. Check out the below code snippet which demonstrates how to write the set property's behavior and use it:

var carName: String = "Default Car"

get() = field

set(value) {

if (value.matches("[^A-Za-z0-9]".toRegex())) {

field = value

} else {

throw IllegalArgumentException("Special characters are not allowed")

}

}

//Usage

println(hatchBack.carName) // Prints Default Car

hatchBack.carName = "Polo"

println(hatchBack.carName) // Prints Polo

hatchBack.carName = "P@&l" // Throws exception

You may have noticed the use of fields. This is called the backing field in Kotlin. The backing field is used to refer to the actual property inside getter or setter. This field can only be available within getter and setter while trying to refer it's own value. In a simpler work backing field is an internal support property that will hold the properties temporary value.

Note: Remember if the property provides any setter kind of a function property must be declared using var. You can use val only and only if it's a constant property or has a getter function only. It's very much logical that immutable property will always use val and not var.

Do not confuse properties with getter and setter over normal function. You could argue that in the above example where a check for the special character is performed before storing the actual value, should it be a function? How would you distinguish that you should define a property of a function?

To identify whether it is property or function you should see that you want to define behavior or a state? The property holds the data hence it should always define the state, for example, weight, color, carType, etc. While function is a behavior of an object, for example, blowHorn(), break(), etc.

What if you don't want to expose setter outside the class. Marking property private will make property invisible for the object and it can only be accessed inside the class. In this case, you should always mark setter as a private. For example:

var someProperty: String = "Some name"

private set(value) {

// Your logic

}

Properties have few more possibilities, for example, overriding the property and delegated property which will cover in a dedicated section later in this book.

Types of constructors

If we think of Java, it has a very strong representation of constructors. It gives full control of how we want to create objects. They are a little bit verbose sometimes. Kotlin has quiet the same rule and way of defining object creation. Kotlin has one primary constructor, one or more secondary constructors mainly. It can also have a private constructor if the developer wishes to do that.

Implicit primary constructor

Kotlin does not force to provide an explicit constructor for your class. If the class does not provide any constructor Kotlin generates a default constructor for you. This implicit constructor has no parameters and can be straight away invoked while creating objects. Let's see the example:

class Person1 {

var name: String = ""

var age: Int = 0

var address: String = ""

}

// Invokes implicit default constructor

val person = Person1()

Primary constructor

A class may or may not have a primary constructor. If there is no constructor, just like Java Kotlin will also inject internally a default primary constructor for you:

class HatchBack constructor(numberOfDoors: Int, colour: String, driveType: CarType = CarType.MANUAL)

A primary constructor is always part of the class header and is followed by a class name. It can take 1 or more parameters. In the above code, the constructor has no visibility modifiers or annotations, hence the constructor keyword can be removed and syntax can be shortened:

class HatchBack (numberOfDoors: Int, colour: String, driveType: CarType = CarType.MANUAL)

The main difference compares to Java's constructor is that in Kotlin primary constructor cannot do any further computation on the data. So where should you write such instructions?

Kotlin provides the initialization block which is immediately executed by the runtime when the object is created. There can be multiple initialization blocks and each block is represented with the init keyword. Take a look at the below code:

abstract class Car(val numberOfDoors: Int, val colour: String, val driveType: CarType) {

init {

println("Iteration one for making your abstract car")

}

init {

println("Iteration two for making your abstract car")

}

}

class HatchBack constructor(numberOfDoors: Int, colour: String, driveType: CarType = CarType.MANUAL) : Car(numberOfDoors, colour, driveType) {

init {

println("Making your hatchback car")

}

}

//Output

Iteration one for making your abstract car

Iteration two for making your abstract car

Making your hatchback car

The example is slightly complicated as it involves a little bit of inheritance. But the point here is how Kotlin processes the init calls. The Car class has multiple init calls and they will be processed in the exact same order they are defined. First parent class gets initialized and secondly the subclass. Please do not run into a state where your mind has too many inheritance related question, we have a dedicated section for that.

So since we have to init block to do computation when the object is created, the next question could arise is, can you access the constructor parameter into init block. The answer is YES. The init block gets access to constructor parameters by default. Do the below change in your Hatchback class:

class HatchBack constructor(numberOfDoors: Int, colour: String, driveType: CarType = CarType.MANUAL) : Car(numberOfDoors, colour, driveType) {

init {

println("Making your hatchback car with $numberOfDoors doors, " +

"$colour colour. It has $numberOfWheels wheels and is of $driveType type")

}

}

But having access to the constructor parameter inside the init block does not mean you can access those parameters anywhere in the class. Class level properties can be accessed anywhere in the class and constructor parameters are not class level properties. Kotlin provides very clean syntax to make constructor parameters as class level property. You just need to mark them with val/var modifier and it will automatically behave as class level property. The constructor parameter marked with val/var can be used initializer block and also can be used anywhere in the class. The rule for a modifier is the same here. val makes immutable property and var makes mutable property. Let's understand with a simple example.

class Person(val name: String, age: Int, address: String) {

init {

println("Constructing person object with name $name. Age is $age and lives in $address")

}

fun printPrettyPerson() {

/*Only name is accessible in entire class. Other params not marked as val are not visible in other areas of class*/

Person object with name $name. Age is $age and lives in $address"

}

}

In the above code snippet, the only name is accessible outside init remaining two properties age and address cannot be accessible outside init.

Secondary constructor

As we saw earlier in this section, a class can have one or more secondary constructor. A secondary constructor is defined with the keyword constructor. There is no need to have a name for any of the constructor in Kotlin. Kotlin uses a keyword to indicate which is constructor and which is the function.

Let's take an example of a Student class where the secondary constructor is provided.

class StudentV1 {

constructor(name: String, studentId: Int)

}

// Gives compilation error as StudentV1 class only offers secondary parameterised constructor.

//val student = StudentV1()

// Valid object creation

val student = StudentV1(name = "Hardik", studentId = 1)

StudentV1 has no primary constructor and only provides secondary constructor. In this case, the object cannot be created by calling a default constructor. It is mandatory to pass parameters that are required for the secondary constructor. If you try to instantiate the object using the default constructor, it will result in a compilation error. StudentV1 only provides one secondary constructor. Let's declare a class which provides multiple secondary constructor along with its primary constructor:

class StudentV2(name: String) {

constructor(name: String, studentId: Int) : this(name) // Direct delegate to primary constructor

// Indirect delegation to primary constructor by calling another secondary constructor.

constructor(name: String, studentId: Int, courseId: Int) : this(name, studentId)

}

val student1 = StudentV2(name = "Hardik")

val student2 = StudentV2(name = "Hardik", studentId = 1)

val student3 = StudentV2(name = "Hardik", studentId = 1, courseId = 10)

Observe here how both secondary constructors are chaining themselves to the primary constructor. It's a rule that if any class provides primary constructor, its secondary class has to delegate it to the primary constructor directly or indirectly. And object student1, student2, and student3 are all valid declarations.

A key difference between primary and secondary constructor is that parameters of the secondary constructor cannot be accessed outside the scope of constructor block. Even if you specify with val/var modifier Kotlin does not allow accessing parameters of the secondary constructor to use outside its scope. This is only possible for the primary constructor. The reasoning behind this is secondary constructor can be multiple and they can have a variety of arguments. A class cannot be that flexible where it can accommodate any set of properties at any point in time.

Note: When a class has to init block and a secondary constructor then an init block will always get priority. This means instructions within the init block will be processed first and the secondary constructor will be processed.

Private constructor

There can be a situation where the object of the class cannot be created outside the class. For example, the Singleton class. If the correct singleton class needs to be achieved you have to restrict the object creation outside the class. In this case, private constructor comes to rescue. To define a private constructor you just need to mention private keyword preceded by a constructor keyword. Check out an example below:

class SecretClass private constructor(val secretWord: String) {

companion object {

fun newInstance(secretWord: String) = SecretClass(secretWord)

}

}

fun main() {

// Gives compile time error

val obj = SecretClass("Kotlin")

val secretObj = SecretClass.newInstance("Kotlin")

println(secretObj.secretWord)

}

Above SecretClass provides a private constructor. So it cannot be initialized outside. An attempt to do that results in a compile-time error as shown in the above main function. A newInstance function is introduced so that newInstance function can be resolved statically and inside that function, an object is created. A property secretWord is marked as val, this makes it a class level property and becomes accessible using object itself:

Note: Constructors also support default parameters. But if all the parameter constructor has provides default valuers, then Kotlin generates additional constructor with no parameters. And this automatically generated constructor uses default values to initialize the class. The official documentation says that there are libraries who create the object using parameterless constructor and by doing so Kotlin makes the process easier for them.

Interfaces

By this point in the chapter, we all are very clear with the purpose of classes, constructors, how objects are created, etc. The interface is also very important while designing good software. The interface helps you to achieve abstraction. They are also a great candidate to achieve decoupling. Decoupling an implementation is a great way to achieve scalability in your system. The interface in Kotlin is very much similar to Java 8 interfaces. Java 8 interfaces broke the stereotype and allow developers to provide a default implementation to methods in it. Before Java 8 interface can only hold constants and abstract methods. No implementation was allowed.

Kotlin's interface can have default implementation as well. And since Kotlin is 100% interoperable with Java 6 and above. You get this feature for your Java 6 compatible legacy system as well. Let's take an example and try to understand more.

Defining an interface

You can define an interface by a keyword interface followed by its name. An interface usually follows upper camel case convention for their name. Take a look at the below Fruit interface:

interface Fruit {

val name: String

fun getContainedVitamin(): List<VITAMIN>

fun showBenifits() {

println("I am low in fat, sodium, and calories and have no cholesterol")

}

}

A Fruit interface contains property and two functions. Property cannot hold any value in the interface this is the main difference compared to Java 8 interfaces. Functions may or may not have implementations. A function without an implementation becomes an abstract function and has to be implemented by the class which is implementing the interface. All interfaces have visibility public by default. You don't need to define it explicitly with the keyword. The interface can have an empty function as well where it does not have anybody.

Implementing an interface

An interface cannot be used until you implement it. The interface is not a class and cannot be instantiated directly with a constructor. The interface has to be implemented by some class and class has to adhere to the contract which interface has. A class has to provide implementations to all abstract functions which are defined inside an interface.

class Apple : Fruit {

override val name = "Apple"

override fun getContainedVitamin(): List<VITAMIN> =

listOf(VITAMIN.A, VITAMIN.B_ONE, VITAMIN.B_TWO, VITAMIN.B_SIX, VITAMIN.C)

}

A class named Apple is defined which is a Fruit. Apple class is responsible to provide implementation to all properties and functions of Fruit interface. If there is a need where you have to have a property that should hold some value you can only do it using getters and setters. For example:

interface Fruit {

val whoAmI: String

get() = "Food of God"

// Other properties and function

}

Inheritance

Inheritance is one of the four fundamental principal of any OOP language. The interface is a way to achieve reputability in OOP. Inheritance makes sense whenever there is an Is-A relationship between classes. For example, Vehicle is a class and Car is a class. Car should inherit Vehicle only if in your system Car is a Vehicle.

In Kotlin any class by default extends Any class. You don't need to specify it explicitly. Any class is not like Java's Object class. Any class is simple and has only 3 functions. equals(), hashCode() and toString(). Because each and every class extends Any they automatically get all these three functions.

Let's say there are two classes A and B. Class B extends class A, and if class A is not extending any of the class then it's called single inheritance:

open class Vehicle(name: String, wheels: Int)

class Car(val name: String, val numberOfDoors: Int, val colour: String) : Vehicle(name, 4)

In the above code, Car extends a Vehicle class. Unlike Java you do not need extends keyword to indicate inheritance. I would give plus point to Java here. By using the keyword extends and implements it makes more clear and readable. Here Vehicle is called a parent/superclass and Car is called child/subclass. Child class has to provide all the required parameter to parent class' constructor. Here name is chained to parent class and we are passing hardcoded value to indicate the number of wheels as 4.

Did you spot a brand new keyword open? In Kotlin all classes are and functions are by default final. This means they cannot be extended or overridden. If you want any class to be extended you have to explicitly provide a keyword open. Making class and function final is considered best practice in programming. Kotlin is highly inspired by The Effective Java norms. And Item #17 says Design and document for inheritance or else prohibit it. According to this norm Inheritance sometimes breaks the rule of encapsulation. Subclasses don't know the complete information about the superclass while overriding any function. And in this situation, the Effective Java norm says you should prohibit the inheritance. Marking classes final is the only way to disallow classes to be extended.

When their class has secondary constructor syntax to call the parent class' constructor slightly changes. Each secondary constructor that subclass provides has to initialize the base type by calling its constructor. A subclass can delegate base type constructor call by calling another secondary constructor of its type:

class CarV2 : Vehicle {

constructor(name: String, numberOfDoors: Int, colour: String) : super(name, 4)

}

Effective Java has been taken so seriously that even functions within the class are by default final. Similar to class you need to use the open keyword to indicate that function can be overridden:.

open class Vehicle(name: String, wheels: Int) {

open fun blowHorn() {

println("Peep…peep…")

}

}

class CarV2 : Vehicle {

constructor(name: String, numberOfDoors: Int, colour: String) : super(name, 4)

override fun blowHorn() {

println("Pom…pom…")

}

}

val vehicle: Vehicle = CarV2("Polo", 4, "White")

vehicle.blowHorn()

To override a function base class has to use override keyword followed by a function writing syntax. If and only if the function which you want to override is marked as open, Kotlin allows overriding it. Otherwise, the compiler will complain.

Note: If a final class marks anything open, Kotlin compiler ignores it and open keyword has no effect inside a final class hence cannot be overridden. On the contrary, if the class is an abstract class and has an abstract function inside, all abstract functions can be overridden by default. No need to specify an open keyword in this situation.

There can be a situation where you want to call the base class' function inside your overridden function. You need to use the super keyword to indicate that you are referring to superclass' implementation. For example:

override fun blowHorn() {

super.blowHorn()

println("Pom…pom…")

}

Overriding a property is very much similar to overriding a function. Property can be overriding if it is inside the open class and marked as open as well:

open class VehicleV2(name: String, wheels: Int) {

open val makeYear = 2000

}

class CarV3(name: String, numberOfDoors: Int, colour: String) : VehicleV2(name, 4) {

override var makeYear: Int = 2019

get() = field

set(value) { field = value }

}

The interesting thing is property can be marked as val in the superclass, but the subclass can override it and change it to var. This is possible because val property usually is of getter type and overriding with var adds setter function as well on top of it. This is perfectly fine and convenient as well.

Another important topic to know about inheritance is the initialization or calling order of properties or functions. Take a look at the below code:

open class VehicleV2(name: String, wheels: Int) {

init {

println("Initialising VehicleV2")

}

open val makeYear = 2000.also { println("Defining make year as 2000") }

}

class CarV3(name: String, numberOfDoors: Int, colour: String) : VehicleV2(name, 4) {

init {

println("Initialising CarV3")

}

override var makeYear: Int = 2019.also { println("Defining make year as 2019") }

}

fun main() {

val vehicleV3: VehicleV2 = CarV3("Polo", 4, "White")

}

//Prints

Initialising VehicleV2

Defining make year as 2000

Initialising CarV3

Defining make year as 2019

What we have here is both the classes VehicleV2 and CarV3 has to init block and we have put additional println() statement to track the execution flow. The order will be base class initialization will take place first for both constructor and property and later on the subclass.

Visibility modifiers

Visibility modifiers are your best friend which will help you to achieve encapsulation. It helps your data to be exposed only where it's required. They also sometimes called access specifiers or access modifiers. In Kotlin classes, constructors, properties, functions, and objects can have visibility modifiers. Kotlin offers four various visibility modifiers i.e. private, protected, internal and public. If no modifiers are provided it defaults to the public.

Let's understand each of the modifiers in detail:

private: Accessible only inside the class.

protected: Visible inside the class as well as in subclasses as well.

internal: Visible everywhere in a module.

public: Accessible everywhere anytime.

For protected modifier property or a function is only visible if it is a class that has defined or a subclass. Assume there is a class Vehicle that has protected property type. And Vehicle is a part of packageOne and there is another class called Aeroplane which is inside packageTwo. Aeroplane can access property type if it's a subclass of Vehicle. Any other function or class or even object cannot access it outside the package. To understand this scenario further follow the below steps:

Create a Vehicle.kt inside package visibilityModifiers.packageOne and put the below code inside:

package visibilityModifiers.packageOne

enum class Type {

ROAD, AIR, WATER

}

open class Vehicle(name: String, wheels: Int) {

protected open val type = Type.ROAD

}

Create another class and name it Car.kt inside package visibilityModifiers.packageTwo:

package visibilityModifiers.packageTwo

import visibilityModifiers.packageOne.Type

import visibilityModifiers.packageOne.Vehicle

class Aeroplane(val name: String, val numberOfDoors: Int, val colour: String) : Vehicle(name, 3) {

override val type = Type.AIR

}

fun main() {

val vehicle = Vehicle("SomethingMoving", 4)

// Gives you compilation error

println(vehicle.type)

}

Now if you see class Aeroplane can access the property type since it is a subclass but in the same file, the object of a Vehicle class cannot access its property because the main function is outside the package.

Kotlin has a new modifier named internal. This is interesting. Assume we need to build a library and we are creating a few extension functions in it. Now extension functions are ideally declared as top-level functions anywhere in the module. If you ship this library it's possible that your client has visibility of your extension function. That is the nature of the extension function. What if you want to prevent it? The best way to prevent visibility of anything outside the module is to mark them as internal. This indicates compiler that if classes, constructors, properties, functions, and objects are marked as internal cannot be visible outside the module.

Note: Top-level functions cannot be marked as internal.

Data classes

Nowadays we end up creating classes just to hold data. We don't put much of logic here. For example, we make an HTTP network request get the request back. By using an object mapper library, for example, Jackson and GSON we map the raw data to model classes, basically a POJO. Kotlin calls them data classes. But they are not just the replacement of POJOs. They offer much more functionality compare to POJO. With data classes you automatically get below four features:

equals()/hashCode(): Also this takes care of equals and hashCode contract.

toString(): Prints object in a readable format

componentN(): Function which will help to de-structure the object.

copy(): Makes copying the object and value easy.

data class Recipe(val name: String, val ingredients: List<Ingredient>)

data class Ingredient(val ingredient: String)

equals()/hashCode()

The contract between equals and hashCode is very crucial in the context of object comparison. Compiler highly relies on this these two functions. You would argue that we don’'t need to worry about it, as most of the IDEs automatically writes this on behalf of us. It’'s not tedious. But the moment you introduce a new member and change the structure of a class, you have to ask IDEs again to write them for you. With data class, you don’'t get that problem. It internally takes care of this.

toString()

It's a very simple function, it prints the object in a readable manner. And you don't have to explicitly call toString() function. Just mention the object and print statement automatically takes care of it. See how we have instantiated a complex object below and still easily prints the object with its value in a pretty way:

val friedChips = Recipe(

name = "Fried Chips",

ingredients = listOf(Ingredient("Potato"), Ingredient("Oil"), Ingredient("Salt"), Ingredient("Pepper")

)

)

println(friedChips)

//Prints

Recipe(name=Fried Chips, ingredients=[Ingredient(ingredient=Potato), Ingredient(ingredient=Oil), Ingredient(ingredient=Salt), Ingredient(ingredient=Pepper)])

componentN()

The componentN() function is the key behind the destructuring declaration of a class. For each property, Kotlin generates corresponding componentN() function and while creating/catching the object it can be decomposed into individual properties. You can not explicitly write componentN() function in your data class. It's not allowed:

val (name, ingredients) = Recipe(

name = "Fried Chips",

ingredients = listOf(Ingredient("Potato"), Ingredient("Oil"), Ingredient("Salt"), Ingredient("Pepper")

)

)

copy()

Yet another small but useful feature. It allows you to copy one object into another without much of a hassle. Let's simply take an example to understand:

fun main() {

val friedChips = Recipe(

name = "Fried Chips",

ingredients = listOf(

Ingredient("Potato"),

Ingredient("Oil"),

Ingredient("Salt"),

Ingredient("Pepper")

)

)

val cheeseFriedChips = friedChips.copy(ingredients = friedChips.ingredients

.toMutableList().apply { add(Ingredient("Cheese")) })

}

You need to keep a few rules in mind while dealing with data classes:

You must define the data class with a primary constructor with at least one parameter. Data class cannot be generated without a primary constructor.

All primary constructor parameters must be marked as val or var.

Data classes cannot be abstract, open, sealed or inner

Data class cannot be extended

Data class can implement or extend the class. But the class which is to be extended must not be data class.

Nested and inner classes

Classes can be written as a nested or inner class. Both classes are written inside the existing class.

Accessing outer class inside the nested and inner class

The difference between nested and inner class is, an inner class can access the property and function of the outer class. Inner class always has a reference to outer function. Syntax to define nested and inner class is the same. Inner class only takes an extra keyword, that is, inner.

class Outer {

var outerNum = 10

class Nested {

var innerNum = 20

fun doSomething() {

//println("Outer num is $outerNum") Gives compilation error

println("Inner num is $innerNum")

}

}

inner class Inner {

var innerNum = 20

fun doSomething() {

println("Outer num is $outerNum") // Allowed

println("Inner num is $innerNum")

}

}

}

Initialising nested and inner class

The nested class can be initialised initialized without creating the object of outer class but the inner class can only be initialised initialized with the object of outer class:.

fun main() {

val nested = Outer.Nested()

nested.doSomething()

val inner = Outer().Inner()

inner.doSomething()

}

Enum classes

Enums are great while it comes to type safety. Enums are thread-safe. In it's simplest form enum can look like below:

enum class CarType {

MANUAL, SEMI_AUTOMATIC, AUTOMATIC

}

Enum classes can also hold values for each enum instance. For example:

enum class CarTypeV2(val type: Int) {

MANUAL(1), SEMI_AUTOMATIC(2), AUTOMATIC(3)

}

And this values can be used later while some decision making process:

fun getCarType(typeCode: Int) {

when (typeCode) {

CarTypeV2.MANUAL.type -> println("Car has manual handling")

CarTypeV2.SEMI_AUTOMATIC.type -> println("Car has semi automatic handling")

CarTypeV2.AUTOMATIC.type -> println("Car has fully automatic handling")

}

}

The enum classes usually have two more properties which are used more, for example, ordinal and name. Ordinal returns the index number of the order that instance holds in all enum declarations. For example, CarTypeV2.SEMI_AUTOMATIC.ordinal will return 1 as it’'s in 1st position in all enum instances. Similarly the CarTypeV2.AUTOMATIC.name will return it’s name, that is, i.e. AUTOMATIC..

Enum classes are very rich in Kotlin. Can you imagine enum constants behave as if they are some classes and can provide behavior as well? Yes, it's possible. Let's grasp it:

enum class Numbers(val num: String) {

ONE("1") {

override fun inRoman(): String = "I"

override fun inSpanish(): String = "uno"

},

TWO("2") {

override fun inRoman(): String = "II"

override fun inSpanish(): String = "dos"

},

THREE("3") {

override fun inRoman(): String = "III"

override fun inSpanish(): String = "tres"

};

abstract fun inRoman(): String

abstract fun inSpanish(): String

}

//Can be called as

println(Numbers.ONE.num)

println(Numbers.ONE.inRoman())

println(Numbers.ONE.inSpanish())

Note: Enum classes can go a bit further and get complicated when they implement any interface. Enums cannot extend the class though.

Sealed classes

The official documentation says that sealed classes are used to represent the restricted class hierarchy. It’'s to create a group of classes. It basically dis allows developers to create new classes which that does not belong to a similar group. To give you an example, consider a state machine problem. For states, machine states are always predefined. We are aware of all the possible states well in advance. This can be represented as a sealed class. One more example which you can easily understand is a network state. While making network calls you can break it down in states like pre loading, loading, success, and error. This can also be a great example of sealed classes.

Defining sealed class

Sealed classes are also treated as an extension of enums. Enums also represent the group. But it is always loose representation. For example, in the previous section, we wrote getCarType() function which was having when the function to check the type. What if you pass a type that is not a member of CarType enum. One can add as many enums as they want and your system will not have any idea. How do you control such a situation? A sealed class is your savior. Let's understand with an example:

sealed class CarType {

object Manual : CarType()

object SemiAutomatic : CarType()

object Automatic : CarType()

}

The above code snippet indicates that there can be only three types of Car, that is, Manual, SemiAutomatic and Automatic.

Using sealed class

Now try to write a function which checks the type using a standard when function:

fun getCarType(carType: CarType) {

when (carType) {

CarType.Manual -> println("Car has manual handling")

CarType.SemiAutomatic -> println("Car has semi automatic handling")

CarType.Automatic -> println("Car has fully automatic handling")

}

}

Imagine a situation where getCarType() is called on multiple places. Up until this point, sealed classes will not sound exciting to you. Imagine now in future you want to add a new type to sealed class CarType, that is, DualClutch. The moment you add a new type to a sealed class, it will start complaining everywhere. With the sealed class you don't need to write else cases for your when function.

Points to remember:

A sealed class is abstract and cannot be instantiated

A sealed class is not allowed to have non non-private constructors. Its constructor is by default private.

All the subclasses of the sealed class have to define in the same file.

Wrapping up

With this last topic of sealed classes, the first phase of learning a Kotlin is complete. At the end of the chapter, you have learned about variables, functions, various types of classes, inheritance, etc. All these topics are the fundamentals of the language. We will use this learning in the upcoming chapter and developing our Android application. Kotlin basics have quick learning curve. If you have knowledge of any OOP language, learning Kotlin can be very quick for you. Remember Kotlin is a JVM language and in the end, you get .class file. Kotlin generates more optimized instructions compare to traditional JVM so at some point code execution is faster.

This chapter is fun to learn but in the next chapter, we will learn more advance topics and unleash the power of Kotlin. The upcoming chapter will be full of thrill and exciting topics. You learn about type system, lambda functions, null safety, co-routines, etc. Keep looking and don't settle.

Pop quiz

We learned quiet a good basics of Kotlin. It’s time to brush up the concept. Take a quiz and evaluate yourself. All questions are from the topic which we have learned so far in this chapter. There are total 10 questions and each questions has four options. Out of these four options only one is the correct answer. You need to choose that. I am sure you know all of them. Go crush them all. All the best!

Which of following code will give you compilation error?

val hexBytes = 0xF_42_40

val bytes = 0b1111_01000010_01000000

var num: Int = 10

val amount:Float

Option a and b

Option c

Option d

None of the above

Which of the following is false for val?

Value cannot be changed for any object initialised using val

val can only be used with lazy

Using val you can create thread safe object if custom getter is not provided.

val cannot be null

What is foo in below code?

val foo = {

println("Hello from the curly braces")

}

A return value

An object

A function

Higher order function

What is to in 1 to "Sunday"?

Lambda function

Infix function

Extension function

Top level function

Which is the correct usage of lateinit?

lateinit val user : User

lateinit var user: User?

lateinit var user: Float

lateinit var user: User

What will be the output of following two println statements?

fun main() {

cast(10)

cast("Hardik")

}

fun cast(data: Any) {

println((data as? String)?.length)

}

Prints

null

6

Prints

2

6

Compilation error

throws NullPointerException for cast(10)

What will the output of following code?

class Recipe{

lateinit var name:String

}

fun main() {

val recipe=Recipe()

println(recipe.name)

}

Compilation error

Throws runtime exception UninitializedPropertyAccessException

Prints null

Prints empty string.

What is wrong with below code?

class Person(val name: String, age: Int, address: String) {

fun printPrettyPerson() {

println("Person object with name $name. Age is $age and lives in $address")

}

}

constructor keyword is missing from class header

All constructor parameters should be of val type.

Age and address cannot be accessed inside printPrettyPerson() and it will give compilation error

Nothing is wrong, class will work as expected.

From following which is not true about the constructor in Kotlin?

Kotlin can have private constructor.

Data class must have at least one constructor parameter.

Properties of secondary constructor can be accessible outside the class if marked with val type.

Primary constructor can have public and private parameters.

Which is not true about the sealed classes?

Sealed class is abstract and cannot be initialised.

Sealed class can have multiple sealed class as their sub class. D.

Sealed class can have both public and private constructor.

Sealed class can be extended.

Answers

Option C is the correct answer. That means val amount:Float cannot be compiled. There is no value provided at the time of initialisation. Both val and var should have value at the time of declaration except the var is not marked as lateinit.

All the options are true, except option D. val can be null as well. Usually, when you get a value either from function call, or some API call and you are trying to catch it using val, it could be null.

Another interesting thing about val is it’s thread safe only and only if you haven’t provided your own get() function for property/object.

foo is actually a function. We can also declare function which way in Kotlin. The foo is a function that returns no value i.e. Unit. It also does not have any parameters. foo can be called as foo() or foo.invoke()

Correct answer is B. It is an infix function. Infix function can be an extension function or a member function only. It cannot have more than one parameter. And those parameter cannot have default value as well.

Option D is the correct answer. lateinit cannot be initialized with type val unlike Option A. It should cannot be a nullable type. And lateinit cannot be used with primitive types like Int, Float, Double, etc.

It will print, null and followed by 6. So correct answer is option A. The fun cast takes argument of type Any, so it will accept both 10 and “Hardik”. But since we have used safe cast operator here i.e. as? While trying to cast value 10 of type Int, to type String, as? will return null and hence length property will not be accessible. But the null value will be returned which println() function will print it.

Correct answer is Option B. It will throw UninitializedPropertyAccessException,as the property is marked with lateinit and it’s not initialised at the time of accessing it. To check if the property is initialised or not you can use ::recipe.isInitialised. This will return true if the property is initialised else false.

The class will not compile. The constructor parameter which are not marked with val, are not accessible other than init{} block. printPrettyPerson() is a member function and not the init{} block, hence it cannot access property age and address.

Correct answer is option C. The parameter of secondary constructor cannot be accessed outside of the constructor. Irrespective of whether they are val or not, you simply can’t do it. You have to chain them with primary constructor and this way it may become available throughout the class.

Option C is the correct answer. Sealed classes in Kotlin cannot have public constructors. All constructors are by default private.

CHAPTER 3

Go to the Depth of Kotlin

"Any fool can know. The point is to understand."

― Albert Einstein

Learning the basics of Kotlin can be very quick. But knowing how it works and some of the advance topics, for example, higher-order functions, delegations, etc. requires patience and practice. Kotlin is an OOP language but very much influenced by functional programming. Most of you might be coming from Java background, which requires a great shift in the way you look at the code. I also come from a Java background and knew very little about function programming when I started with Kotlin. Understanding a few topics was no easy deal for me. We as a Java developer always think in an OOP way and while working with any of the languages unknowingly we develop a pattern of thinking in a specific manner. So for this chapter, I would advise, that I do not think Kotlin as a Java substitute only. Do not try to compare with possible Java code you think in the back of your mind while looking at the Kotlin syntax. Kotlin has great powers. You can write code for so many things e.g. JavaScript, Node.js, iOS applications, etc. Now programmers of the above platform think in a completely different compare to Java developers. For this chapter assume you are learning a completely new language. Relax your mind and widen the horizons of your imagination ability. Put Java and it's syntax aside. We are going to enter into the world of function paradigm. We will use all our learning whatever we have learned and created in the previous chapter.

Structure

This chapter covers the following topics:

Kotlin's type of system

Delegated properties

Lambdas

Higher-order functions

Collections API

Take advantage of generics

DSL-Type safe builders

Using annotations

Kotlin's type of system

Everything is class and everything is a type in Kotlin. Kotlin type system is very beautiful, the more you understand the more you will like it. Type safe programming is considered the best approach. With type safety, you can avoid a lot of runtimes error. To give you an example, try to think of NullPointerException in Java or undefined is not a function in JavaScript. They are nightmares for programmers. We hate debugging those issues. The type system helps you avoid such types of issues. You can get to know about errors while writing the code itself. There are no runtime surprises because of any missing type.

Because of the robust type system in Kotlin, you get many benefits. You can easily avoid NullPointerException, you can identify a function that returns nothing but can throw an exception. Kotlin's type inference feature is highly reliable and depends on its type system internally. There are plenty of types in Kotlin, for example, primitive, non-primitive which you might be knowing already. And then there is type safety against null and also there are types like Any, Nothing, Unit, etc. Let's understand the null safety first.

Null safety

NullPointerException aka NPE is already notorious in the Java world. It doesn't require any explanation. We all know this exception with no messages on the console. If it's an Android application, this causes the crash and you see infamous. Unfortunately, the application <APP_NAME> has stopped. With Kotlin you don't need to worry much about this issue now. This can be very much controlled while writing the code itself.

The difference here for Kotlin is, Kotlin allows you to express nullability of any variable or object or function's return type. And from that point onwards while you try to access that property or variable compiler knows that this can be null or not. Imagine a function that returns the occurrence of any given character for any string. A Java code for this can look like below:

private int getCountForCharacter(final String data, final char characterToSearch) {

int count = 0;

if (data != null) {

for (char c : data.toCharArray()) {

if (c == characterToSearch) {

count++;

}

}

}

return count;

}

At the moment this method looks fine. But did you notice what if the developer does not put the null check? Your function will throw NPE. Also, the code is a bit verbose, nested if blocks, etc. Let's see how Kotlin code can handle this situation. Below is the Kotlin code in action:

private fun getCountForCharacter(data: String?, characterToSearch: Char): Int {

return data?.filter { it == characterToSearch }?.count() ?: 0

}

The smile on a face? I am sure. The syntax could be shorter if we write the function as an expression. Anyway, conciseness is not the point here. You need to observe String?, data?.filter and ?:. These question various question marks pretty much summarize Kotlin's capability to deal with nulls. Let's understand each every bit now.

data: String? Indicates that data is of String type and it can be null. This is possible when you are dealing with third party API or data is coming from some another function that was returning null. Having said that variable or property using only String (or any other class without question mark operator), cannot be null. Kotlin compiler will guarantee you this. So whenever you are not sure whether the object can be null or not, mark your type with? If the compiler catches that there is a possible situation where an object can be null and object is having a non-nullable type, the compiler will give you the compile-time error. For example:

val data: String?=null // OK and can be null because it's marked with ?

val data: String=null // Not allowed and will give you compile time error

Any type can be defined as a nullable and non-nullable type. For any type Kotlin always generates two types, for example:

var str: String = ""

var str: String? = null // Internally supports String? type

var num: Int = 10

var num: Int? = null // Internally supports Int? type

var recipe: Recipe = Recipe("Burrito", listOf("Corn tortilla", "Cooked Rice", "Beans"))

var recipe: Recipe? = null // Internally supports Recipe? type

Safe call operator

As we know using ? operator, we can mark any type as nullable. How do you check whether the object is null or not? Kotlin supports traditional null checks using if and else. Below is an example:

fun getLength(data: String?): Int {

return if (data != null) {

data.length

} else {

0

}

}

But Kotlin believes in conciseness. So they introduced a safe call operator (?.). With safe call operator same code can look like:

fun getLength(data: String?): Int {

return data?.length ?: 0

}

Basically, the safe call operator executes the latter part if the object is not null. You can multiply use a safe call operator. Check out some example below:

data?.length // Gives you length if data is not null

data?.filter { } // execute filter function if data is not null

data?.let { } // execute let function if data is not null. Similarly use apply, run, also etc.

map?.get(0) // Try to get the data for the key from HashMap if Map is not null

A safe call operator supports chains as well. For example, employee?.company?.department?.designation. A safe call operator can help you evaluate multiple nulls and in the end, gets the job done. You can also assign values to property that are using safe calls to check nulls. For example:

employee?.company?.department?.designation=getDesignation()

And of course, the assignment is skipped if the employee or company or department is null. Imagine how many nested if checks will you save now.

Elvis operator (?:)

With the safe call operator, we can easily command the runtime to process the instruction only if the object is not null. What is object is null and we want to do something else. For this Elvis operator is used. While accessing any null object if you want to return the non-null object you can use Elvis operator to do so. On the right-hand side of Elvis operator you provide any non-null value which will be returned by Elvis operator. For example:

data?.filter { it == characterToSearch }?.count() ?: 0

In the above code snippet, data is a String. And this code returns the occurrence of any character into the data String. Now filter function will be called on it only if data is not null. Similarly, count function will only work if data is not null and the filter function is processed. But what if data is null. Here Elvis operator plays the role. In the end, we just put Elvis operator and provided 0 as it's non-null value. This means if the String is null it's length is 0. Not only String but if anything to the left of Elvis operator is null the right-hand side which is non-null will be processed and return as a result.

You can have any expression or function to the right-hand side of the Elvis operator. Check out below example:

//If the object is not null same object can be returned else initialize and return the newly created object.

recipe = recipe?.let{ it } ?: Recipe("SomeFood")

Non-null assertion (!!)

Apart from using a safe call operator, there is another way to convert any value to a non-null type. You can use double exclamation marks to forcefully convert types to non-null types. Since you are using double exclamation marks (!!) it can bring you a double surprise. While using non-null assertion if the type is null it throws NullPointerException. Kotlin compiler never wants you to use this type of syntax. But there can be a situation where you are sure that value will not be null but you are still forced by compiler to handle the nullable properties. You can use !! cautiously at that time.

Any, Nothing, and Unit

Since in Kotlin everything is type it's very much important to understand what type system uses to understand that in a class hierarchy what is the top most class, what sits in the bottom and what represents no value? In this class hierarchy Any is the top most class and Nothing is the bottom most class. Unit is just a type like any other class to represent invalid or empty. Try to visualize the class hierarchy in below Figure 3.1:

Figure 3.1

You can see how every class is somehow child of Any class. And Nothing is the bottom most class for any class. Please do not misunderstood that Any is direct super class of other classes like String, File, etc. There can be other classes in hierarchy. Let's take some time to understand this special types.

Any

Any is the super most class in Kotlin. You can relate it with Java's Object class. Every class is derived from class Any. Any can be nullable, for example, Any?. The signature of class is:

public open class Any

This class is marked with open modifiers i.e. this can be extended further by other classes. The member function of Any class is equals(), hashCode() and toString(). That's the reason all classes in Kotlin gets this function by default. The class Any also has lot's of useful extension function which will is used in your day to day development work. For example, javaClass, also, apply, let, run, to, etc. See how some of this function signature looks like:

// Returns the java class at runtime

public inline val <T : Any> T.javaClass: Class<T>

// Here T represents Any

public inline fun <T, R> T.let(block: (T) -> R): R

Nothing

Exact opposite of Any is Nothing. Where any is the super most class in class hierarchy, Nothing sits at bottom in class hierarchy. It is the last class in hierarchy. Nothing has no value. It cannot be instantiated as well. So why would you use Nothing? And if it cannot be used where else we can use it? Assume you have below situation in your code, where your function is processing something and does not return anything. But it input is wrong function can throw and Exception:

fun showPerson(person: Person) {

println(person.name ?: throw Exception("Error"))

println(person.age ?: throw Exception("Error"))

println(person.address ?: throw Exception("Error"))

}

How would you make it clear to the developer. Since there is no checked exception Kotlin compiler will never complain. You should specify Nothing as a return type. A function with return type Nothing cannot return any value but can only throw an Exception so developer will know that the function can go wrong. The Nothing can be used as return type for above function and the same function signature now look like below code:

fun showPerson(person: Person): Nothing

Let's take one more example. Imaging you have below data class which represents some response from network calls and with instance of this response class you want to read the data property:

// Can return null and data will be inferred as nullable type.

val data = response.data

// Cannot return null and compile will infer nonNullData as non nullable property

val nonNullData = response.data ?: fail("Data is not available in response")

The function fails returns Nothing and this helps Kotlin compiler to infer anything as non-nullable. And this saves you from NPE.

Unit

Unit is Kotlin's void. It is used as a return type for function where the function is not returning anything. Unit is managed automatically. That is if you do not mention return type for any function, by default it has derived as Unit. The compiler will do it internally. There is still a difference between void and Unit. The unit is a type and not just a keyword. The unit is a singleton class. Look at the below piece of code from Kotlin's source code:

public object Unit {

override fun toString() = "kotlin.Unit"

}

Delegated properties

In Kotlin properties can do a lot more than just storing the data. You can specify the behavior to the property. This is a great and very much unique feature compare to traditional OOP languages. Delegated properties let you specify whether you want to store the data in the database, cookies or shared preferences (for android application). You can also specify the condition as delegation where the property will accept the value only if the input matches the criteria. For example, assume there is a requirement that a Person object has name and address properties and these properties cannot hod special character. Usually, you will write some sort of function to achieve this. And you will call this function inside your setter function. With delegated property, you can achieve such requirements in a much better way.

The delegated property has below syntax:

class Example {

var p: String by Delegate()

}

Here p is a property that delegates its behavior to Delegate() method's objects. The keyword is used to indicate that property p of String type have Delegate() as it's delegation. Delegated property internally has getValue() and setValue() which is used as a delegation during each read and write operation for the respected property.

There are some common properties that Kotlin team has already taken care of. They are part of the standard library. The pre-bundled delegated properties are:

lazy: Value is evaluated and assigned only once throughout the lifecycle.

observable and vetoable: They are listeners who get notified on every change in the value.

Storing property in a map: Store value in a map instead of separate fields.

Let's try to understand each of them with an example.

lazy

We learnt a bit about lazy in chapter 2 section Being late or lazy. We know what it is. But at that time we didn't know that it's delegated property. Let's take the same example and understand more:

val sayHello by lazy {

println("Inside lazy")

"Hello"

}

The lazy is a function that takes a function as a parameter and applies the thread-safety mode LazyThreadSafetyMode.SYNCHRONIZED which makes it synchronized. And in a synchronized block it checks if the property is initialized or not. If not initialized then execute the function/initializers store the return value and return the value to the property. And if the property is initialized just return the old stored value. This can only be used with val specifier.

observable and vetoable

Many times we want to observe the property and listen to the changes. And if there is any expected change we react upon that change. To achieve this you ended up writing the whole observable pattern. Kotlin team knows this pain and they have observable properties to offer. Let's take an example of such property and try to understand how it works. Check out below code which uses observable property:

class Recipe {

var name by Delegates.observable("<no name>") { prop, old, new ->

println("$old -> $new")

}

var ingredients: List<String> by Delegates.observable(listOf("Salt")) { prop, old, new ->

println("$old -> $new")

}

}

fun main() {

val recipe=Recipe()

recipe.name="French fries"

recipe.ingredients= listOf("Potato","Oil","Salt","Pepper")

}

//Output

<no name> -> French fries

[Salt] -> [Potato, Oil, Salt, Pepper]

The above code snippet has two properties, name, and ingredients. The observable is the inline function for class Delegates. It has two parameters the initial value and the handler function which is represented as lambda. Whenever there is any change a handler is invoked and you have full control to perform whatever you want to do inside that handler. You get both new and old value inside the handler. If you see in the function when we assign the name to the recipe object it prints both the old and new value as per our implementation. The print statement is part of the lambda handler for the name property. Similarly, it goes for the ingredients property where the list of old and new ingredients is printed on the console.

vetoable

A slightly different version of observable is vetoable. The vetoable allows you to intercept the assignment. You can use vetoable to store the value inside property only if it meets your criteria. Assume there is a person class and you want to update the age value only if it is greater than 0. You can use the vetoable property as demonstrated in below code snippet:

class Person {

var age by Delegates.vetoable(0) { prop, old, new ->

new >= 0

}

}

fun main() {

val person = Person()

person.age = 18

println(person.age) // Prints 18

person.age = -10

println(person.age) // Also prints 18

}

The result of handler function should be Boolean. That's the part of function vetoable function signature. The Boolean value is used by compiler further. If it returns true compiler updates the value otherwise rejects the input. In above code 18 is accepted as value but when -10 is given as input the value is rejected and the old value 18 is persisted.

You might this, how do I know that new value is accepted or not. There should be some error at runtime if something goes wrong. This is a valid expectation and hence Kotlin allows you to throw an exception if the new value is not matching your criteria. Check below code where name property can only be alphabets. Here the code will throw Exception is the value contains anything other than alphabets. Below code does the same:

class Person {

var name by Delegates.vetoable("Unnamed") { prop, old, new ->

if (new.matches("^[a-zA-Z]*$".toRegex())) true else throw Exception("Name can only contain alphabets")

}

}

fun main() {

val person = Person()

person.name="Hardik"

println(person.name) // Prints Hardik

person.name="Hard!k" // Throws Exception as there is special character which is not valid.

println(person.name)

}

The vetoable property a fantastic feature, isn't it?

Binding property from the map

Many developers use the map as a dictionary to store the data. This could include examples like parsing JSON and storing it into the map. Or returning multiple values through the map. While reading value from the map wouldn't it be nice to map the entire map against some object? The map delegated property reads your class property and finds those properties against its keyset. Imagine you have the map to store car related information and you want to bind that entire map to the Car class which you have created. How would you do it? Take a look the below code snippet which does the same:

data class Car(val carMap: Map<String, Any>) {

val name: String by carMap

val manufacturer: String by carMap

val noOfDoors: Int by carMap

val otherSpecs: List<String> by carMap

}

fun main() {

val carMap = mapOf(

"name" to "Mustang GT",

"manufacturer" to "Ford",

"colour" to "Black",

"noOfDoors" to 2,

"otherSpecs" to listOf("Power 285 - 312 bhp", "0-60 mph 0 - 5.6 Secs", "Fuel Economy 28 - 35 mpg")

)

val car = Car(carMap)

println(car.toString())

}

Check out int Car class how the map is bound to the property using delegation. Each property name has to match with the key of the map entries. The properties which are not matching with key entries in the map will be skipped during mapping and will not hold any values.

Note: Currently the delegated property (including standard delegated properties) does not work with data class and constructor parameters. It has to be a normal class level property.

Writing custom delegated property

Apart from readily available delegated properties, what if you want to write your own. You can specify the behavior by writing your own custom delegated property. Let's consider a property that we want to write in a File. And while accessing that property we want that property to return the same value which is in File. You can easily achieve this by writing some File extension functions. But with delegated property is such an elegant solution. Let's take a look. What we want is the following behavior:

fun main() {

var fileData = ""

println(fileData)

fileData = "This is a new file content" // We want this data to go into file

println(fileData) // fileData should return value from file.

}

Currently fileData is nothing but a simple String and cannot achieve what we need. Let's create a delegated property named FileStorageProperty. FileStorageProperty will basically provide implementation for property being read and write. FileStorageProperty is a class and needs to implement operator getValue() and setValue(). Signature of these two functions needs to be understood. Take a look at following code:

operator fun getValue(thisRef: Any?, property: KProperty<*>): String

operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String)

The getValue will be invoked while reading the value and setValue will be used while writing the value for your property.

Create a class with name FileStorageProperty and it should take two parameters as shown in code:

enum class WriteMode {

STANDARD, APPEND

}

class FileStorageProperty(private val fileName: String, private val mode: WriteMode = WriteMode.STANDARD)

Now in FileStorageProperty class let's write getValue and setValue functions. Please refer below code snippet to refer how getValue and setValue needs to be added. Every important part is highlighted and discussed below:

class FileStorageProperty(private val fileName: String, private val mode: WriteMode = WriteMode.STANDARD) {

init {

with(File(fileName))

{

if (!exists()) {

createNewFile()

}

}

}

operator fun getValue(thisRef: Any?, property: KProperty<*>): String {

return File(fileName).bufferedReader().use { it.readText() }

}

operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {

when (mode) {

WriteMode.APPEND -> {

File(fileName).appendText(value)

}

else -> {

File(fileName).writeText(value)

}

}

}

}

The class can have to init block which you can utilize to perform anything at the time of object creation. The operator keyword is required for each getValue and setValue. Also, the sequence of the parameter will be the same. Only the return type for getValue and value property for setValue depends on the type for which you are writing delegated property. To make it more interesting our delegated property also supports mode like STANDARD and APPEND. Using we can either overwrite the file or append the content in the file. You can try out these properties using the below code:

fun main() {

var fileData by FileStorageProperty("file.txt",WriteMode.APPEND)

fileData = "This is a new file content" // We want this data to go into file

println(fileData) // fileData should return value from file.

fileData = "This is a updated new file content" // We want this data to go into file

println(fileData) // fileData should return value from file.

}

Lambdas

Lambdas are nothing but functions without a name. They are called anonymous functions in the programming world. They do not bound to any identifier. All the modern programming languages, for example, Dart, Haskell, Java, Lisp, Ruby, Scala, PHP, Python, Swift including Kotlin supports this kind of feature. As we learned in the previous chapter, section top-level functions, they are the first-class citizen in Kotlin. Functions can be passed as parameters as well in functional programming. We have already seen syntax around the usage of Lambdas. All Kotlin standard functions which we have learned so far e.g. let, apply, run uses nothing but lambda function. Let's understand everything about lambda functions bit by bit. Check out below code:

val sayHello = { println("Hello Kotlin") }

How do you see the above code? Is it a property? Is it a function? The syntax does not look obvious. Well, it's a function. The right-hand side of = operator is the anonymous function. And we have bind the anonymous function to the name sayHello. Ideally, you won't name a lambda function and they are straight away passed as a parameter as if they are handler for some event. So how do you call the above function? It can be called in two ways as shown below:

fun main() {

sayHello()

sayHello.invoke()

}

This is the lambda in the simplest form. The invoke operator is a special operator that allows you to specify the default function for any class. In the above code, the block for sayHello is nothing but a default function for that instance. And that's why Kotlin allows you to use invoke operator and process the function.

Now, what if we want to define the anonymous function with parameters? Is it possible? Yes, let's see how we can achieve it:

val calculateAverage = { data: Array<Int> ->

data.sum() / data.size

}

In the above code snippet, we have defined a calculateAverage function which takes Array<Int> as an argument and calculates the average for the values inside it. The data argument is processed inside the function body. Inside the body of a function, you can provide any implementation as per your requirement. Within the scope of a function, everything is just like usual function implementation. You can call it the way sayHello is called. Either user function notation () or use invoke operator. Below code demonstrates both the approaches:

fun main() {

println(calculateAverage(arrayOf(10, 20, 30, 40, 50)))

println(calculateAverage.invoke(arrayOf(10, 20, 30, 40, 50)))

}

The calculateAverage function can also be written in a bit different way, which is as follows:

val calculateAverage: (Array<Int>) -> Int = { data ->

data.sum() / data.size

}

In the above syntax we first define the signature of a function and after that implementation of it as an expression. Both approaches are valid. Here Kotlin won't be able to infer the types, just like any other function signature you have to explicitly mention parameter type and return type. You can skip specifying the return type because in this case, Kotlin will consider it as a Unit. Lambda declaration can end up being a strange and interesting syntax sometimes. Below code highlights a few more variations:

// With no parameter and no return type

val sayHello: () -> Unit = { println("Hello Kotlin") }

// Function with no parameter but returns Long

val getCurrentTime: () -> Long = { Date().time }

// Same getCurrentTime function but with lateinit specifier

lateinit var getCurrentTime: () -> Long

fun main(){

// Defining implementation of lateinit getCurrentTime

getCurrentTime = { Date().time }

//Calling the function

println(getCurrentTime())

}

Lambdas can also used as an extension functions. For example:

val isEmail:String.()->Boolean={

this.matches("^[\\w-_\\.+]*[\\w-_\\.]\\@([\\w]+\\.)+[\\w]+[\\w]$".toRegex())

}

Here is Email will be an extension function over class String. The String.() indicates that extension function is over class String and take no parameter. The →Boolean indicates it results into Boolean value. Within {} is an implementation of is Email function and this refers to the String instance on which the function is called. These type of usage of lambda function is also known as lambda as expression.

Initially, you will pick up a bit slow with these syntaxes. But over the period of time, Lambda syntax will be real fun for you.

Lambdas and functional interfaces

We understood the basics of lambdas. How do we start using them? The best way to start using more lambdas is through function interfaces. A function interface is where there is only a single abstract method is defined. They are also known as SAM interfaces. The function interface will have only and only one abstract method. Many platforms and frameworks which support GUI have event listeners. Event listeners are the best example of function or SAM interfaces. And this type of interface can easily be represented as a lambda function. Runnable in Java and View.OnClickListeners in Android is an example of functional interfaces. Imagine you have an interface in Java which has a single abstract method and looks like below:

//Interface inside Calculable.java file

public interface Calculable {

void onCalculate(Integer number, Integer result, OperationType operationType);

}

//Enum declaration inside OperationType.java file

public enum OperationType {

SUM, SUBTRACT, MULTIPLY, DIVISION

}

Now above interface are used by a class called Gibberish which is in Kotlin and looks like the following code:

class Gibberish(private val number: Int) {

private lateinit var calculable: Calculable

fun setOnCalculateListener(calculable: Calculable) {

this.calculable = calculable

}

fun performCalculation(newNum: Int, operationType: OperationType) {

val result = when (operationType) {

OperationType.SUM -> number + newNum

OperationType.SUBTRACT -> number - newNum

OperationType.MULTIPLY -> number * newNum

OperationType.DIVISION -> number / newNum

}

if (::calculable.isInitialized) {

calculable.onCalculate(number, result, operationType)

}

}

}

Gibberish class provides some implementation which returns some results based on the operation you request. The performCalculation function has an implementation that returns the result. It also has a function which allows you to set listener which notifies you with the updated result on every requested operation. Following Kotlin's lambda code will help you here to concise the syntax and represent it in a much more readable manner:

val g1 = Gibberish(20)

g1.setOnCalculateListener(Calculable { number, result, operationType ->

println("$number is changed to $result because $operationType was requested")

})

Tip: Android Studio or IntelliJ Idea has inbuilt lint which will indicate the possible usage of a lambda with a warning sign.

Higher-order functions

Higher-order functions are all around lambda functions. Higher-order functions are a function that can take a function as an argument or return a function as a result. Using functions as a parameter and return type function is the proper functional programming concept. In Kotlin as we have seen before functions are first-class citizens so you can move them in your application as a parameter. They can be stored as a value and data structures. Once you get rid of the lambda syntax understanding the higher-order function is very easy. Let's start with a simple example.

To define higher-order function-function types are used by Kotlin. Functional type is the way of declaration of a higher-order function. Function types represent and match with the function's signatures. Suppose you want to represent a function type that takes two Int parameters and returns Boolean. You use something like (Int, Int) → Boolean. It uses () parenthesis to denotes function parameter and → to indicate return type. Kotlin has great support for Null. You can define nullable function type as well i.e. ((Int, Int) → Boolean)?

Using lambdas as the callback

A higher-order function can be used to replace the way you write callbacks using the interface. You will use lambda as well. Very often you will have a scenario where you have a function that does something asynchronously and you need some sort of callback from that function. You will be writing an interface and set it inside the class, won't you? Kotlin sees this situation as just a matter of calling a function, and what function to call is decided on the basis of the parameter you are passing. Take a look at the below function signature. How do you interpret it?

fun getData(url: String, callback: () -> Unit)

It's a function which takes two parameters a String named url and a function which is named as callback within the scope of getData function. The callback function takes no parameter and returns nothing. Recall your first lesson and for example, in lambda, we have sayHello function which was just a block of code. And later on, the sayHello function was called by using the invoke operator. Similarly, the callback function can be called by invoke the operator whenever we need it. Below is the elaborated code for getData function:

getData(url: String, callback: () -> Unit) {

println("Waiting to finish the long running operation")

//Your long running operation

val t = Thread(Runnable {

Thread.sleep(5000) // Simulating delay

callback.invoke()

})

t.start()

}

What you just learned is the simplest example of a higher-order function. When the function which you want to pass as a parameter has its parameters the syntax looks a bit odd on the first look. Take a look at the below syntax and try to find out what the function could be doing:

fun perform(first: Int, second: Int, func: (Int, Int) -> Int): Int {

return func(first, second)

}

Find the complete breakdown of the above function in below Figure 3.2:

Figure 3.2

Now let's say param first and second be 10 and 20 numbers and func be any function which will perform something on those two numbers, for example, add and subtract operations. We can define add and subtract as lambda expressions using below code:

val add = { x: Int, y: Int -> x + y }

val subtract = { x: Int, y: Int -> x - y }

The add and subtract functions take two-parameter of Int type and return an Int result. That's the exact signature func parameter of perform function requires. Now we have perform the function and other two functions ready which can be passed as a parameter and output can be tested. So how do you call perform function? There can be two ways of calling perform function. Either you can pass a function as a parameter just like any other function or call it as a lambda style.

Passing function as a parameter.

In following code snippet you both add and subtract are demonstrated and these are passed as parameter, just like any other property or an object:

fun main() {

println(perform(first=10, second = 20, func = add))

println(perform(first=10, second = 20, func = subtract))

}

Passing function lambda style

If you choose the code to be concise and want to keep caller function and parameterized function together you should pass the parameter lambda style. Within this approach, there can be little variations. For example, pass the lambda function inside the function parenthesis () or pass them outside the function parenthesis. You can lift the lambda parameter outside the parenthesis. Style 2 makes the code very much readable and widely used by the community. Check out below code to understand the difference between both the styles:

fun main() {

println(perform(10, 20, { x, y -> x + y })) // Style 1

// OR

println(perform(10, 20) { x, y -> x + y }) // Style 2

println(perform(10, 20, { x, y -> x - y }))

// OR

println(perform(10, 20) { x, y -> x + y })

}

Tip: IntelliJ IDEA or Android Studio will show you a lint suggestion which tells you to move lambda parameter out of the parenthesis. See Figure 3.3:

Figure 3.3

Higher order function as an extension function

Kotlin does allow you to write higher order function as an extension function. Above perform function can be written as an extension function for Int class. Here you need to apply your learning from previous chapter's Extension function section. Below code is an example of how perform function can be written as an extension function:

fun Int.perform(other: Int, func: (Int, Int) -> Int): Int {

return func(this, other)

}

As a change, we will remove the first param because the first param will be replaced by the number itself which is referred to as this inside the function implementation. And you can call the function like below:

fun main() {

println(10.perform(20) { x, y -> x + y })

}

Returning a function

Yet another feature of the functional programming language is, returning a function. Yes, functional programming languages can return a function just like any other property or an instance. Syntax wise we are familiar with how to specify a return type for any function. Function returning a function with no param and no return type has the following signature. Let's assume we have a function that returns us another function based on some condition. We will stick to simple examples.

Let's put down our requirement point by point:

The function should take a type as a parameter.

The function should return a function which takes two Int parameter and returns Int.

The function internally decides which function to return based on the type.

Below code identify which function to return based on the operation type you pass in as a parameter:

enum class Operation {

ADD, SUBTRACT, MULTIPLY, DIVISION

}

fun getOperation(type: Operation): (Int, Int) -> Int {

return when (type) {

Operation.SUBTRACT -> { x, y -> x - y }

Operation.MULTIPLY -> { x, y -> x * y }

Operation.DIVISION -> { x, y -> x / y }

else -> { x, y -> x + y }

}

}

If you notice when function checks for each condition and returns a lambda expression for respective type. Now let's catch the returning function inside our main function. Just like any other function we will catch the value using below code snippet:

val division = getOperation(Operation.DIVISION)

To use the return value or to actually make a function call, you can either use invoke operator or use normal function syntax. See the usage in below code:

fun main() {

val division = getOperation(Operation.DIVISION)

println(division.invoke(10, 20))

val multiple = getOperation(Operation.MULTIPLY)

println(multiple(30, 5))

}

Using callable reference

Kotlin always promises conciseness and readability. And the Kotlin team has done every possible effort to achieve these two promises. A callable reference is the ability to pass a property as a parameter. It also allows to pass functions as a parameter. Passing a property and parameter is something we already know. So what is so special about callable reference and how it's different compare to what we already know. Consider below code:

val bots = listOf(

"Optimus Prime", "Ratchet",

"Fallen", "Bumblebee", "Megatron", "Starscream", "Ironhide",

"Barricade", "Demolisher"

)

val autoBots = listOf(

"Optimus Prime", "Roadbuster", "Ratchet",

"Bumblebee", "Ironhide"

)

fun isAutoBot(bot: String) = autoBots.contains(bot)

fun main() {

println(bots.filter { isAutoBot(it) })

println(bots.filter(::isAutoBot))

}

Notice the :: operator which is used to refer the isAutoBot function. But wait, where is the parameter for ::isAutoBot? That's the magic of callable reference. Callable references are often used with higher-order function. :: converts function into the value it's a syntax for such type of usage.

You can also access the class property using callable references. It's known as member reference expression. Let's assume we have a list of recipes and we want to sort them based on preparation time they need. We will select the recipe which takes the least amount of time to prepare. Create the following class structure to store the data and prepare a list of it:

data class Recipe(val name: String, val ingredients: List<String>, val prepTime: Int)

fun main() {

val recipes = listOf(

Recipe(

name = "Fried Chips",

ingredients = listOf("Potato", "Oil", "Salt", "Pepper"),

prepTime = 40

),

Recipe(

name = "Eggplant Salad",

ingredients = listOf("Eggplant", "Tomato", "Onion", "Salt", "Pepper", "Oregano", "Parsley"),

prepTime = 30

)

)

}

Since you probably do not have any clue that you can use member reference expression here you might code using lambda function as depicted below:

println(recipes.minBy { it.prepTime })

There is nothing wrong doing this way, it is still doing the job and will give the desired result. And with member reference you can write the same behavior as demonstrated in below code:

println(recipes.minBy(Recipe::prepTime))

A member reference expression is very useful where you want to directly pass the function as an argument without using the lambda function. Sometimes this makes code less redundant.

Note: Callable reference or member reference only works with non-lambda style function calls. Within the lambda block, you cannot use this syntax. If you have notice recipes.minBy(Recipe::prepTime) is not a lambda function call. The property is just passed as a normal function parameter.

Inline function

Many amongst you are introduced to the concept of inline function back in the early days of your programming in C++. We create functions to reuse the logic. And we call functions to use that code. We all know that functions go in the stack during their execution. The more functions we call the more stack operations to take place. We write a lot of utility functions just so that we can call it from anywhere and anytime. If functions are marked as inline compiler just copy the code on all the places where it's called. And during runtime, it is just a series of instructions. No stack operation is involved in such a scenario. Languages like Java removed this feature because JVM used to do a lot of optimizations at the time of creating byte code. They might be inlining the code internally but for a developer, there is no provision to mark the function as inline. So why Kotlin introduced it back? Why we are learning it again?

Kotlin stores its lambda expressions and higher-order function as an object somewhere in memory. Many times these lambdas or function passed as a parameter does very simple things and doesn't justify the need for a proper function which should hamper stack operations. If you do so, you end up increasing the memory overhead. Imagine such lambdas and higher-order function are part of some loop! Let's take an example to understand it better. We will revisit bots and filtering Autobot example here. Have a look at the following code:

fun main() {

val bots = listOf(

"Optimus Prime", "Ratchet", "Fallen", "Bumblebee", "Megatron", "Starscream",

"Ironhide", "Barricade", "Demolisher", "Bonecrusher", "Sentinel Prime"

)

val cepticons = listOf("Optimus Prime", "Roadbuster", "Ratchet", "Bumblebee", "Ironhide", "Sentinel Prime")

val timeTakenWithoutInline = measureTime {

println(bots.filterCepticons {

cepticons.contains(it)

})

}

}

private fun List<String>.filterCepticons(predicate: (String) -> Boolean): List<String> {

val filteredList = mutableListOf<String>()

for (item in this) {

if (predicate(item)) {

filteredList.add(item)

}

}

return filteredList

}

The filterCepticons function which evaluates the boolean expression and adds an element in the filteredList item if the expression returns true as a result. The bots and cepticons are the two data sets. Nothing is wrong in the above code. It will compile and run as per your expectations. But is it optimized? How do we know that there is a possible optimization? Well, filterCepticons is an extension over type List<String>. On top of this, it is also a higher-order function. Kotlin suggests to use inline function while you define a higher-order function which takes the lambda type parameter. So let's test this. We will create an exact copy of filterCepticons function and name it inlinedFilterCepticons. And don't forget to add the inline keyword in the function signature. The new function will look like the following:

private inline fun List<String>.inlinedFilterCepticons(predicate: (String) -> Boolean): List<String> {

val filteredList = mutableListOf<String>()

for (item in this) {

if (predicate(item)) {

filteredList.add(item)

}

}

return filteredList

}

And the inlinedFilterCepticons can be called from main function as shown below:

val timeTakenWithInline = measureTime {

println(bots.inlinedFilterCepticons {

cepticons.contains(it)

})

}

Try to print both the resulting properties, that is, timeTakenWithoutInline and timeTakenWithInline and you will see the difference.

What's going on behind the scene of the inline function

So you saw the difference inline function is making, and theory says that it replaces function's call with actual code. Let's prove it. Let's try to decompile the code and what it does. Here I will be sharing only limited code of it's decompiled version, but in the IDE you can see the complete code. Following code has limited but sufficient information to know what is going on behind the scene:

public static final void main() {

List bots = CollectionsKt.listOf(new String[]{"Optimus Prime", "Ratchet", "Fallen", "Bumblebee", "Megatron", "Starscream", "Ironhide", "Barricade", "Demolisher", "Bonecrusher", "Sentinel Prime"});

List cepticons = CollectionsKt.listOf(new String[]{"Optimus Prime", "Roadbuster", "Ratchet", "Bumblebee", "Ironhide", "Sentinel Prime"});

List var9 = filterCepticons(bots, (Function1)(new InlineFunctionKt$main$$inlined$measureTime$lambda$1(bots, cepticons)));

System.out.println(var9);

List filteredList$iv = (List)(new ArrayList());

Iterator var15 = bots.iterator();

while(var15.hasNext()) {

String item$iv = (String)var15.next();

int var17 = false;

if (cepticons.contains(item$iv)) {

filteredList$iv.add(item$iv);

}

}

System.out.println(filteredList$iv);

}

You can see, that var9 is holding the result of filterCepticons function. But there is no function call inlinedFilterCepticons which we have written in the Kotlin code. Instead of inlinedFilterCepticons, we see some while loop being added. That's because we have marked inlinedFilterCepticons with inline keyword. So the compiler has replaced that function code with actual logic.

I hope you understand the core of using inline function.

Note: Functions like a filter, map, last, first, count, distinct, groupBy are marked as inline inside the Collections.kt. But I don't think now you should need reasoning behind it.

Collections in Kotlin

According to me the quintessential feature of any language is how easy it allows you to deal with data structures. In Java, it was known as Collections. Collections in Java are a package where classes are available which makes your life easy when it comes to dealing with data structures like List, Queue, Stack, Tree, Map, and Set. They are quite nicely managed. Understanding collections requires a good amount of effort. But it's worth it as you can do so many things. If collections are not used in an appropriate way they can impact the performance of your applications. Java provided basic functionality for its collections classes and interfaces. This left developer to build their logic if they want to use the collections. Developers started writing logic in their way. For example, if you want to search in the List, for that you will iterate through the list using a for or while loop. But novice developer may forget to break the loop once the record is found. This type of basic functions and optimizations are lacking in Java. Kotlin comes up with their Collections package. This Collections package have loads of utility functions and properties, which makes dealing with Collections and fun task for any developer.

Kotlin also offers three main data structures known as List, Set, and Map. The hierarchy is similar to Java's. The type List, Set, and Map are interfaces and there are concrete implementations for this. The initialization and using them is very readable and less verbose. Check out the following code which gives a glimpse how these data structures can be used:

val languages = listOf("Java", "C++", "C#", "COBOL", "Python")

val map = mapOf(

1 to "January", 2 to "February", 3 to "March", 4 to "April",

5 to "May", 6 to "June", 7 to "July", 8 to "August", 9 to "September",

10 to "October", 11 to "November", 12 to "December"

)

val set = setOf(

1 to "January", 12 to "December", 3 to "March", 4 to "April",

10 to "October", 6 to "June", 2 to "February", 8 to "August",

7 to "July", 9 to "September", 5 to "May", 11 to "November"

)

I like this very much and I have never enjoyed using Collections before I used them in Kotlin. As Kotlin mainly supports List, Set and Map let's understand the philosophy of these three core Collection interfaces.

List is an ordered collection of elements. Maintaining the insertion order is the core feature. It can hold n number of elements theoretically. You can access each element or item inside the list using a position. You can iterate over the list. It supports functionality like, add, remove, and update. It can also hold duplicate records. All it cares about is the heterogeneous item in an ordered manner.

Set is a collection of unique elements. You cannot hold duplicate data. If you try to enter a duplicate element, it will not complain but it will discard the entry request. And Set does not respect insertion order. While retrieving an element from Set, you can't expect to have elements in the same order as they were inserted.

Map is a collection where data is recorded using the concept of key and value. Each and every element is mapped against a key. It can represent one to one mapping very much reliably. Also, you can map duplicate elements, but the key has to be unique.

These interfaces are generics that means all type of data is being supported. It can support basic types like String, Int, and any other classes which are created by you. These three interfaces support all basic operations which other languages provide. We won't cover all the functions for each of the types. We are going to focus on some cool functions which can be useful and interesting for your development activities.

Note: Many functions for this Collection framework uses Extension function to provide interesting features. Based on its type functions can vary. Also, all functions support any data type. Some of the functions depend on the mutability of the object. For example, if the instance is immutable you cannot functions like add, or remove if it's List, similarly, you cannot add elements in Set or Map if they are immutable. While initializing these types you can specify if you want mutable instance or immutable.

List

Each collection provides two ways of initializing. You can ask for either mutable or immutable list instance. The mutable reference can be modified later on by using functions like add, remove, etc. But an immutable object cannot be modified. Check out the following code:

fun main() {

val immutableLanguages = listOf("Java", "C++", "C#", "COBOL", "Python")

// Add is not available here

immutableLanguages.add("")

immutableLanguages.toMutableList()// Returns mutable list as a new instance

val mutableLanguages = mutableListOf("Java", "C++", "C#", "COBOL", "Python")

mutableLanguages.add("Go") // You can modify this list

}

This is basic initialization and doesn't require much effort to understand how it works. The listOf function provides an immutable instance of List and hence cannot be modified. Few functions which can modify the list, will not be available at all. While on using mutableList function you get access to functions that can modify the list e.g. add.

Functional initializers

There is also another interesting way available to initialize the List. This approach is known as functional initializers. Here you can the size of the list and a lambda function, where you can specify the logic of initializing the list. To understand it better, check out the following code:

fun main() {

val list = List(5) { "Item -> ${it.plus(1)}" }.also { println(it) }

val mutableList = MutableList(5) { "Item -> ${it.plus(1)}" }.also { println(it) }

}

List and operator overloading

What if I tell you that you can use + and – operator while working with List. Yes, + operator will concatenate the list and – operator will help you to remove the single or multiple items from the List. Check out the code. Such a simple yet cool trick and a feature. Who won't love this?

fun main() {

val languages = listOf("Java", "C++", "C#", "COBOL", "Python")

val newLanguages = listOf("Kotlin", "Go")

val awesomeLanguages = languages + newLanguages

println(awesomeLanguages) // Prints [Java, C++, C#, COBOL, Python, Kotlin, Go]

val boringLanguages = awesomeLanguages - newLanguages - listOf("Java", "C++", "C#", "Python")

println(boringLanguages) // Prints [COBOL]

Figure 3.4

Tip: Use Alt + Enter to put long list of arguments in separate lines as shown in Figure 3.4.

Reversing a List

Java is passed by value, but for data structures like List and Array, it can be still modified if someone is holding the reference of those instances. For example, if there is List A and B, B is holding a direct reference of A, what will happen to B if List A is modified? Does B gets updated as well? I tried this exercise in Kotlin and below is a result.

There are two lists worldRecipes and reversedWorldRecipes. As the name suggests, reversedWorldRecipes is holding a reference of worldRecipes but in reverse order. We modified the worldRecipes after reversing it. And as I was expecting, reversedWorldRecipes automatically updated it and maintained the reverse order as well. Have a look at the following code to get more understanding:

fun main() {

val worldRecipes = mutableListOf(

Recipe("Jerk Chicken", 60),

Recipe("Paella", 30),

Recipe("Frito Mist", 50),

Recipe("Hamburger", 20)

)

println(worldRecipes)

val reversedWordRecipes = worldRecipes.asReversed()

println("Reversed world recipes")

println(reversedWordRecipes)

// This only works if [worldRecipes] is mutable

worldRecipes.add(Recipe("Hakka noodle",15))

println("Reversed world recipes after updating master list")

println(reversedWordRecipes)

}

/** Output

[Recipe(name=Jerk Chicken, cookingTimeInMinutes=60), Recipe(name=Paella, cookingTimeInMinutes=30), Recipe(name=Frito Mist, cookingTimeInMinutes=50), Recipe(name=Hamburger, cookingTimeInMinutes=20)]

Reversed world recipes

[Recipe(name=Hamburger, cookingTimeInMinutes=20), Recipe(name=Frito Mist, cookingTimeInMinutes=50), Recipe(name=Paella, cookingTimeInMinutes=30), Recipe(name=Jerk Chicken, cookingTimeInMinutes=60)]

Reversed world recipes after updating master list

[Recipe(name=Hakka noodle, cookingTimeInMinutes=15), Recipe(name=Hamburger, cookingTimeInMinutes=20), Recipe(name=Frito Mist, cookingTimeInMinutes=50), Recipe(name=Paella, cookingTimeInMinutes=30), Recipe(name=Jerk Chicken, cookingTimeInMinutes=60)]

*/

Sorting Collections

Sorting is another important functionality which we use frequently during any application development. There were definite ways in Java to sort the collections. But it was very verbose. Java was using the concept of Comparator and Comparable to sort the Collections. Kotlin also uses something similar. But because of lambda functions, the syntax is very much concise and readable. On top of this Kotlin is also providing by default functions to reverse the list, or to arrange the list in ascending or descending order. In Kotlin sorting can be one line of code for your classes. Take a look at the following examples, which cover the sorting example for List, Set, and Map.

Sorting List

Let's imagine we have a list of type Recipe. Recipe type holds the name of the recipe and cooking time in minutes. And we want to sort the given list based on the preparation type. How would you do that in Kotlin? You don't need to implement the Comparator for this and make your data class bulky. Just use a function sortedBy which takes a comparator as an argument. The sortedBy function doesn't even trouble you to create an instance of the comparator, you just specify the criteria and it generates Comparator for you. It's brilliant, isn't it? Check out the following code:

data class Recipe(val name: String, val cookingTimeInMins: Int)

fun main() {

val worldRecipes = listOf(

Recipe("Jerk Chicken", 60),

Recipe("Paella", 30),

Recipe("Masala dosa", 5),

Recipe("French Toast", 8)

)

// Returns sorted immutable list

println(worldRecipes.sortedBy { it.cookingTimeInMins })

val recipeComparator = Comparator<Recipe> { recipe1, recipe2 -> recipe1.cookingTimeInMins - recipe2.cookingTimeInMins }

// Returns sorted immutable list using custom comparator

println(worldRecipes.sortedWith(recipeComparator))

}

/** Both println prints

[Recipe(name=Masala dosa, cookingTimeInMins=5), Recipe(name=French Toast, cookingTimeInMins=8), Recipe(name=Paella, cookingTimeInMins=30), Recipe(name=Jerk Chicken, cookingTimeInMins=60)]

*/

The above code also demonstrates that how can use custom Comparator to specify your comparison criteria. The instance recipeComparator in the code snippet shows how Comparator interface can be used for that. The sortedBy function is a stable sort this means the items having a same value maintains the same insertion order even after sorting.

Note: Do you know that Kotlin has typealiased a lot of Java classes and interfaces? E.g. In the above example, the Comparator is the same Java Comparator but Kotlin has typealiased the name and provide a new logic to the Comparator. See the following code.

@SinceKotlin("1.1") public actual typealias Comparator<T> = java.util.Comparator<T>

Sorting a Map

The instance of a Map can be sorted using a TreeMap data structure. TreeMap has a private instance of Comparator which simply calls the .comparator function on it and returns you the SortedMap as a new instance.

As shown in the following code toSortedMap does the same thing. The toSortedMap function's return type is SortedMap<T> which internally returns the TreeMap<T>, both SortedMap and TreeMap are a subset of Map interface:

fun main() {

val map = mapOf(

1 to "January", 12 to "December", 3 to "March", 4 to "April", 10 to "October", 6 to "June",

2 to "February", 8 to "August", 7 to "July", 9 to "September", 5 to "May", 11 to "November"

)

// By default it will sort using keys and returns instance of TreeMap

println(map.toSortedMap())

println(map.toSortedMap(compareBy { map[it]?.length }))

}

/** Prints

{1=January, 2=February, 3=March, 4=April, 5=May, 6=June, 7=July, 8=August, 9=September, 10=October, 11=November, 12=December}

{5=May, 6=July, 3=April, 8=August, 1=October, 12=November, 9=September}

*/

By default the key of your map becomes the sorting parameter, and your map will be sorted based on the value of your keys. If you don't wish to have this behavior for sorting your map instance, you can always provide your implementation of Comparator. In above code snippet, toSortedMap(compareBy { map[it]?.length }) demonstrates the same. We are sorting the map using it's value's length now.

Sorting a Set

Unlike Map, Set doesn't use SortedSet and TreeSet. It uses different approaches for sortedBy and toSortedSet function. Function sortedBy actually returns List<Pair<T,T>>. Yes, and that's how your new sorted set will maintain it's order. It's a trick which Kotlin is doing. And you can try that on your own. Following code shows how you can apply your Comparator on the Set:

fun main() {

val set = setOf(

1 to "Sunday", 7 to "Saturday", 5 to "Thursday", 3 to "Tuesday",

4 to "Wednesday", 2 to "Monday", 6 to "Friday"

)

println(set.sortedBy { pair -> pair.first })

println(set.toSortedSet(compareBy({ it.second }, { it.first })))

}

/** Prints

[(1, Sunday), (2, Monday), (3, Tuesday), (4, Wednesday), (5, Thursday), (6, Friday), (7, Saturday)]

[(6, Friday), (2, Monday), (7, Saturday), (1, Sunday), (5, Thursday), (3, Tuesday), (4, Wednesday)]

*/

By the way what you can do if you have multiple filter criteria? With Java-style Comparator, you were always able to specify multiple sorting criteria to your collections. Kotlin also does that. Take a good look at the above code snippet again and see how for the second println statement, we are providing two sorting criteria. First, the Set is sorted based on the value, that is, it.second and it gives preference to its keys, that is, it.first. Since Set is nothing but the instance of Pair<T,T> you can refer its element using it.first and it.second.

Sequence vs Iterable

One very important point which I would like to cover as a part of Collections is the difference between Sequence and Iterable. All collections use Iterable. Similarly, you can use sequences with all types of data types. So what is the difference between using Iterable and Sequence? And when should you use what? If you take a look at the signature if these interfaces, they look ditto:

public interface Iterable<out T> {

public operator fun iterator(): Iterator<T>

}

public interface Sequence<out T> {

public operator fun iterator(): Iterator<T>

}

They also offer a similar set of extension functions. For example, filter, map, take, drop, forEach, binarySearch, etc. You name any Iterable function and Sequence will have it. Even the output will be the same. So where exactly the difference lies? Why we have both Iterable and Sequence?

The difference lies between the ways they are implemented. There is a difference between semantics. The documentation says that Sequence and extension function on sequence works lazily. This means until the terminating operators like sum, count, toList are called the Sequence will not trigger. Let's see a simple example and try to understand the problem.

In this little experiment, we are going to take 8 numbers. We call it transactions. We will filter out these 8 transactions if the amount is greater than 100. We will print this filtered out transaction amount and take the first 3 of them. We will do this experiment on Sequence and List and observe the output. The following code shows how it's done using a list that is Iterable. And in both the code I am going to highlight some lines which hold significance:

val transactionList = listOf(5, 120, 10, 73, 805, 2000, 30, 300)

.filter { it > 100 }

.map {

println("Found transaction with amount $it")

it

}

.take(3)

println(transactionList)

/** Output

Found transaction with amount 120

Found transaction with amount 805

Found transaction with amount 2000

Found transaction with amount 300

[120, 805, 2000]

*/

Observe the output, and here is a code that uses sequences to do the same.

val transactionSequence = sequenceOf(5, 120, 10, 73, 805, 2000, 30, 300)

.filter { it > 100 }

.map {

println("Found transaction with amount $it")

it

}

.take(3)

println(transactionSequence)

// Prints something like below

kotlin.sequences.TakeSequence@65ab7765

At the beginning of the section, I said that the output will be the same. But on comparing the output it doesn't look the same.

The output is not the same because we did not invoke the terminal operator. We did not call the terminal operator so Sequence did not work. And that's the reason println statement is still printing Sequence as an object and not its actual output.

Now, let us change println statement a bit as shown in the following code snippet:

// Before

println(transactionSequence)

// Change it to

println(transactionSequence.toList())

// Output is

Found transaction with amount 120

Found transaction with amount 805

Found transaction with amount 2000

[120, 805, 2000]

Now we can see that output is the same. But along with output, you should also observe how many times it prints the filtered amount. The Iterable printed four Found Transaction… statements, but Sequence only prints three. This proves it processes less data.

The reason behind this is Iterable, processes everything top to bottom. If we use Iterable it filters out everything first, then executes another operator on it. And that's the reason Iterable first prints all the four transactions which were greater than 100. But Sequence operators works per element. Sequence somehow already knew that they only need to take out 3 transactions hence it only filtered out 3 transactions and did not process remaining transactions once it found 3 transactions. Smart, isn't it?

When Sequences needs to be used

When we have multiple operators involved to get the final result, for example, filter, map, take in the experiment we did, it makes sense. Since Sequence works lazily it is very much important we only use it when multiple operators or multi-step processing is required on your data.

I did some benchmarking with different data and the different size of the record and following is the output:

Sample size

Iterable (Time in ms)

Sequence (Time in ms)

8

73.7

34.9

19

154.0

48.3

38

63.0

32.4

114

41.9

32.8

1140

42.0

28.0

11400

66.8

32.8

The time which is punched is an average of 5 attempts. I did that for each sample size. I did it on my machine, so it's slightly inconsistent between different sample sizes, but one thing is proved that Sequence takes less time.

Taking advantage of Generics

Developers who have worked on C++ might remember about templates. In Templates, you can pass type as a parameter. That was all about templates. Generics is a similar concept like Templates in Java and Kotlin. You can use type as a parameter for classes, interfaces, functions. Have you ever wondered how the Collections like List, Set and Map supports any data type? It's a Generics behind the scene. In the source code, you might have noticed something like List<T>. This T indicates the generic type. T can be replaced with Int, String, Boolean, or any other type, for example, User, Recipe. T needs to be used along with opening and closing angular brackets <T> to indicate the generic type. Imagine you are creating a bin that can take any type of item. This bin can be represented in a class style using the following code:

class Bin<T>() {

}

But why you should be using Generics? Let's explore.

Why Generics?

Why we should pass type as a parameter? Let's say we have a requirement to put toys in a Bin. You can put and take the toys from the bin. So for that, you might write a class Bin, with the functions like put and take. Put function will take the instance of Toy class as a parameter and take function will return an instance of Toy class. The class can look like the following:

class ToyBin() {

private val space = mutableListOf<Toy>()

fun put(item: Toy) {

space.add(item)

}

fun take(position: Int): Toy? {

return space[position]

}

}

Ok! we are happy with this our class and it does work as per our requirement. Now let's say we want to have a similar bin but the bin which can store candles in it. This bin has the same requirement to put and take the items. You might write a new class CandleBin with the function put and take just like ToyBin:

class CandleBin() {

private val space = mutableListOf<Candle>()

fun put(item: Candle) {

space.add(item)

}

fun take(position: Int): Candle? {

return space[position]

}

}

This works! But is it the right approach. Of course, not. We cannot put aside the concept of reusability in software development and design. Reusability is a very important aspect. Reusability was one of the reasons behind object-oriented programming language. The creation of classes like ToyBin and CandleBin is not scalable. What if you have a new requirement to store medicines and cups in the bin. You can't just keep on creating classes like CupBin and MedicineBin. Wouldn't it be great where you can specify the class Bin about the type of data it can store in it? Generics can help you here.

You can think of a class Bin where you will pass a type T in the form of generics. The new Bin class can look like the following:

class Bin<ITEM>() {

private val space = mutableListOf<ITEM>()

fun put(item: ITEM) {

space.add(item)

}

fun take(position: Int): ITEM? {

return space[position]

}

}

Tip: You can use any word to indicate the Generic type. It does not necessarily have to be T. You can use little meaningful names. In our example, we are using ITEM. ITEM goes in Bin. The widely used convention is to use single character e.g. T for a single parameter, K and V to indicate Key and Value. Sometimes ITEM may confuse you as an enum value.

Now this Bin class can be used with any type. Bin can either store items of type Candle, Toy, Medicine, Cup, or any type. The same thing is explained graphically in below Figure 3.5:

Figure 3.5

Generic and type safety

You might be thinking if you want to use a class for any type of data, why can't you use type Any? Any is the very first class in the hierarchy. The super most class in Kotlin. The Bin<ITEM> can also be used as Bin<Any>. So why don't we use Bin<Any> and spending time to learn and understand generics. Using generic is safe compare to Any. It provided type safety. Any does not provide the type safety. Let's understand this by one more example. Let's consider that we have a mutable list where we want to store Recipe. We can achieve this by either using List<Any> or List<Recipe>. But what difference it creates if List is of type Any or of a specific type. Take a look at the following example:

val anyList = mutableListOf<Any>(frenchFries, lassi, pasta)

println((anyList.firstOrNull() as? Recipe)?.name)

anyList.add("WTF") // Can be done. Compiler will not complain

The anyList is supposed to store only instances of type Recipe, but since we pass type as Any, it can take the instance of any other data type. In the example, you can see how the instance of type String can be easily added. Also while reading the data, the class cast is required. It is using as a class cast operator to read the name of a recipe.

But when we create a List<Recipe> we can only and only use objects of type Recipe. If you try to enter any other type of instance in the List you will get a compilation error. And reading the instance from the list does not require casting. Because it is already known that the type can only and only be Recipe:

val recipeList = mutableListOf<Recipe>(frenchFries, lassi, pasta)

println((recipeList.firstOrNull())?.name)

recipeList.add("WTF") // Gives you compilation error.

recipeList.add(veggieChilly) // Possible because it's a recipe instance

Generics and constraints

When you use generics that means it can be used with any type. But what if you want to restrict the type. For example, you want to create a list but only for type Mammal. Mammal can be an interface or class. See the following code snippet:

class Animal<T : Mammal>(private val animal: T)

Here for class Animal, you can pass either Mammal type or subtype of Mammal. Let's take an in-depth look. Let's create a few types first. I have created four types you can say categories of species:

interface Animal {

val lifeSpan: Int

}

interface Oviparous : Animal {

val eggColour: String

}

interface HasFurOrHair {

val hairColour: String

}

interface Limbless

Out of these four main types, there are few derived types. Let's create a generic class Mammal which is a subtype of Animal and HasFurOrHair. Along with Mammal, I have created another generic class Reptile. And Reptile is a subclass of type Oviparous.

In the previous example of a generic class Animal, any Mammal and subtype of Mammal can be passed. But in our new example, Mammal is a generic class which is a subtype of Animal and HasFurOrHair. How do you set multiple constraints? Kotlin offers a syntax for this. You can use where clause and it let you define the multiple type which will act as constraints. The syntax for this is as following:

class Mammal<T>(private val mammal: T) where T : Animal, T : HasFurOrHair {

fun aboutMe() {

println("""I am a mammal with life span of ${mammal.lifeSpan}

|    and my hair colour is ${mammal.hairColour}""".trimMargin())

}

}

class Reptile<T>(private val reptile: T) where T : Oviparous {

fun aboutMe() {

println("""I am a reptile with life span of ${reptile.lifeSpan}

|    and my egg colour is ${reptile.eggColour}""".trimMargin())

}

}

When you specify the constraint using where the property and functions of both the type become automatically visible for the subtype. Keep in mind that Kotlin's generic will respect the access specifier like private, protected, internal, and public. In the case of interface, things are simple because properties and functions are public. But when it is a class all these concept matters. For example, if HasFurOrHair is a class and the property hairColour is marked as protected, you will not be able to use it inside the Mammal class. But if the same property is marked as internal or public, you can easily access it.

When it comes to implementation, to understand this generic types Mammal and Reptile and it's constraints we have further created classes it will look like the following:

class Human(override val lifeSpan: Int, override val hairColour: String) : Animal, HasFurOrHair

class Whale(override val lifeSpan: Int) : Animal, Limbless

class Snake(override val lifeSpan: Int, override val eggColour: String) : Oviparous, Limbless

Now Mammal can accept Human, but it will not accept Whale. Even though Whale is Animal but our definition of Mammal says it should also have hair or fur. If you try doing something like it is shown in below snippet, the Mammal with Human will work fine and but Mammal with Whale will give you compilation error:

val human = Mammal<Human>(Human(lifeSpan = 70, hairColour = "Black"))

//Below is not possible because Whale is not a type of HasFurOrHair and gives compilation error

val whale = Mammal<Whale>(Whale(lifeSpan = 70))

This is how type safety can save you with the usage of constraints. Now one thing is common in all types like Human, Whale, and Snake. They all are of an Animal type. So if there will be a generic class called AnyAnimal which has a constraint for class Animal. So this AnyAnimal class can accept instances of type Human, Whale, Snake. Check out the below code:

class AnyAnimal<T : Animal>(private val animal: T) {

fun aboutMe() {

println("I am an animal and I can live up to ${animal.lifeSpan}")

}

}

val anyHuman = AnyAnimal(Human(lifeSpan = 70, hairColour = "Black"))

val whale = AnyAnimal(Whale(lifeSpan = 70))

val snake = AnyAnimal(Snake(lifeSpan = 15, eggColour = "White"))

Generics and variance

Another interesting concept which Kotlin offering is variance. What is the variance? Variance is where you define how the type will be used. Variance is related to inheritance as well. It's a bit tricky to understand on the first try. But I am going to give a very practical example. Imagine, there are type RecycleableWaste and Tin. The relationship between these two types is an Is-A relationship. Where Tin is a RecycleableWaste. In Kotlin code they can be represented as follows:

interface RecycleableWaste

class Tin : RecycleableWaste

In the programming world, you can refer to the instance of subtype, as if it is the instance of the supertype. And because of runtime polymorphism, the supertype will automatically be converted into subtype. What I am trying to say is, the following code is possible:

val wasteList: List<RecycleableWaste> = mutableListOf<Tin>()

But we cannot refer to an instance of Tin as an instance of RecycleableWaste. I.e. following code is not possible. Also if try to do so, it will give you compilation error and results into type mismatch error:

// Not possible

val tinList: List<Tin> = mutableListOf<RecycleableWaste>()

What if I tell you that with variance this is possible? Kotlin allows you to refer the subtype with the instance of the supertype. That means val tinList: List<Tin> = mutableListOf<RecycleableWaste>() can be true. There is a catch though. You need to commit to the compiler that you will use the type in a certain way. The concept is known as covariance and contravariance in Kotlin.

Covariance

As I briefly explained above, that Kotlin will let you refer to the instance of child type as the instance of parent type is known as covariance. For example, Tin is RecycleableWaste, so I can safely hold the instance of Tin as if it's the instance of RecycleableWaste. This way val wasteList: List<RecycleableWaste> = mutableListOf<Tin>() is true. And to do something with your generic type, you need to use a keyword, i.e. out. This is called declaration site covariance. Suppose there is a Generic class named Bin. Bin can take any type. In this example, we will use this class either with type RecycleableWaste or Tin. Tin is a subclass of RecycleableWaste. The Bin class has a collect function as well, which will return the last item from the Bin. The Bin class has a private list object which will have all waste items of the given type inside it. Following code shows how the Bin class can look like:

class Bin<out T> {

private val items = mutableListOf<T>()

// Put function will give compilation error.

fun put(litter: T) {

}

fun collect(): T? {

return items.lastOrNull()

}

}

Notice <out T>. This means whatever type you provide here, can only be used as a return type. You cannot use it as a function parameter, within that class. If you try to do so, the compiler will be angry on you, see Figure 3.6:

Figure 3.6

So what benefit <out T> will bring? The answer is very logical. When you specify out compiler will make a note that the type is only be going to be used as return type. This means if you fetch the item from Bin<Tin>, it is guaranteed to return either Tin or any supertype of it. This is safe. Because Bin<Tin> is logically Bin<RecycleableWaste>. So now since we are using <out T> below statement is possible:

val recyleWasteBin: Bin<RecycleableWaste> = Bin<Tin>()

You might be thinking that if we have to use <out T> to make these things possible, how Kotlin List was able to do it? Well, it's not a wonder, it is doing the same thing. Kotlin's collection has defined the List with List<out T>, check out below statement from the codebase:

public interface List<out E> : Collection<E>

Contravariance

Variance in Kotlin's generics is logically correct and follows the OOP principle very much. Most of you would be more interested about contravariance. Contravariance is the concept where the instance of parent type can be referred and used as an instance of child type. Let's see how. The way <out T> indicates that the type can only be used return type. Opposite to that contravariance relies on <in T> which indicates that type can be used only as a parameter. You cannot return the <in T>. It's all about in and out when it comes to variance. We have specified another type to prove this. We call it Bin2. Bin2 is a generic type, that is, Bin2<in T>:

class Bin2<in T> {

private val items = mutableListOf<T>()

fun put(litter: T) {

items.add(litter)

}

// collect function will give compilation error.

fun collect(): T? {

return items.lastOrNull()

}

}

Bin2 can only accept the type as an incoming parameter. If you try to return it, you will get compilation error as shown in Figure 3.7:

Figure 3.7

But without the collect function, we can make the following statement possible. And the compiler will be happy:

val recycleTinBin: Bin2<Tin> = Bin2<RecycleableWaste>()

So now what happens, if I try to use the object recycleTinBin? Bin2 has put function and if you try to put an instance of Tin it will allow you. Because logically it is still correct. You can put the instance of Tin in the Bin2 of type RecycleableWaste. The variance will do the job for you. See the following code:

val recycleTinBin: Bin2<Tin> = Bin2<RecycleableWaste>()

recycleTinBin.put(Tin()) // Only adding Tin is possible

Both covariance and contravariance can live together for the generic class. Imagine two interfaces or combinations of class and interface. One of them offers covariance and others offer contravariance. And your generic type class either extends or implements those classes or interfaces. I have created that scenario here. Let's say there is interface LitterBin, which can only accept and follows covariance using <in T>. And then another interface BinCollector which is a follower of the covariance concept and uses <out T>. Now together they are used by the class DumpingYard and it is a generic class. Both LitterBin and BinCollector has some abstract functionality, which DumpingYard has to provide the implementation for. Both the interfaces and class is as follows:

interface LitterBin<in T> {

fun put(litter: T)

}

interface BinCollector<out T> {

fun collect(): T?

fun collectAll(): List<T?>

}

class DumpingYard<T> : LitterBin<T>, BinCollector<T> {

private val items = mutableListOf<T>()

override fun put(litter: T) {

items.add(litter)

}

override fun collect(): T? {

return items.lastOrNull()

}

override fun collectAll(): List<T?> {

return items

}

}

Along with the above class and interfaces, we still have our old types i.e. interface RecycleableWaste, class Tin and class Plastic. This is a very much real-world scenario. And because of variance, we can represent it so beautifully.

Things will get a bit complex when we look at the implementation, so pay attention! Remember, it is a real-world scenario so try to relate it. The analogy is BinCollector<Plastic> cannot collect items from BinCollector<Tin>. But BinCollector<RecycleableWaste> can collect items from Bin<Tin> or from Bin<Plastic>. Read again, if it's doesn't make sense. This logical relationship is true. Let's look at this analogy in the form of code:

// Creating a dumping yard with some random waste in it.

val recycleBin = DumpingYard<RecycleableWaste>().apply {

put(Plastic())

put(Tin())

}.also {

println("Inside recycleBin")

println(it.collectAll())

}

val tinBin = DumpingYard<Tin>().apply {

put(Tin())

put(Tin())

}

//Possible

//val tinBinCollector: BinCollector<Tin> = tinBin

val tinBinCollector: BinCollector<RecycleableWaste> = tinBin

println("Inside Tin Bin")

println(tinBinCollector.collectAll())

But can the instance of recycleBin can be used as LitterBin<Plastic>? Thanks to contravariance. The code can look like following which is making this things possible:

// Possible because Plastic is RecycleableWaste

val plasticBin: LitterBin<Plastic> = recycleBin

plasticBin.put(Plastic())

plasticBin.put(Plastic())

The recycleBin instance is now referred as plasticBin. And trying to access the instance plasticBin will only allow you to put the items in and not taking item out. Your data is still safe. Your recycleBin of RecycleableWaste still serves the same purpose.

Use site variance or type projection

Both covariance and contravariance have a variance. The one which we learned just now is called declaration site variance. That means the <in T> and <out T> is specified at the time of declaration only. But what if you want to use the type which doesn't offer covariance and contravariance. Kotlin still allows to do that with the idea of use site variance. This means you can declare the variance based on your need at the time of usage. It could be some function parameter where you are specifying the variance of any of the types. In Android application development, we sometimes have a situation where we need to maintain the copy of a specific instance. One instance (let's name is allRecords) keeps track of all the records it has receives so far and another instance has the latest data (Let's call it latestResponse) received from SQLite or a web API. We usually copy/add all records from latestResponse to allRecords. This means we only want to add the data in allRecords, but want to get the data out of latestResponse. Let's have a generic type RecipeBook without variance feature at the declaration site variation. Following code can explain more than words can do:

class RecipeBook<T> {

private val items = mutableListOf<T>()

fun put(item: T) {

items.add(item)

}

fun getAll(): List<T> = items

copyLatestRecipeIntoAllRecipes(allRecipeBook, latestRecipes)

}

fun <T> copyLatestRecipeIntoAllRecipes(allRecipeBook: RecipeBook<in T>, latestRecipes: RecipeBook<out T>) {

latestRecipes.getAll().forEach {

allRecipeBook.put(it)

}

// Won't compile because you're trying to read value from <in T>

//val list: List<T> = allRecipeBook.getAll()

// Won't compile because you're trying to put value from <put T>

//latestRecipes.put(frenchFries)

}

The above code is pretty much self-documented. You can see how the class RecipeBook respected the use site variance AKA type projection.

DSL – Type-safe builders

Sometimes I feel, Kotlin aimed for multiple platforms from the very beginning. It has got some elements and features which can make you happy to any developers irrespective of which platform or language you use. Concise, readability, and expressiveness are some top points that Kotlin team always kept in mind. DSL stands for Domain Specific Language. So which domain are we talking about here? Kotlin's team has not clarified why their name is like that. But my understanding says that some aspect of any platform needs a special treatment. For example, generating layout in Android is different than the convention code. Android application uses XML to generate layout. It is very much painful to generate the layout using core classes and through Java or Kotlin code. One thing which Kotlin team has made clear that DSL can be used to represent hierarchical data structures. What are hierarchical data structures?

Hierarchical data structures are something that can be easily read and processed in the top to the bottom manner and left to right manner. This top to bottom and left to right manner is my invented word for understanding purpose. For example, JSON, or XML or Android's build.gradle file. All this makes sense if you read them top to bottom. When you go through these files from top to bottom you get idea about what this file is doing. The following code will give you a quick idea about how DSL code can look like:

plugins {

id("com.android.application")

kotlin("android")

kotlin("android.extensions")

}

android {

compileSdkVersion(29)

defaultConfig {

applicationId = "com.hardiktrivedi.theinternationaldhaba"

minSdkVersion(23)

targetSdkVersion(29)

versionCode = 1

versionName = "1.0"

testInstrumentationRunner = "com.hardiktrivedi.theinternationaldhaba.RecipeTestRunner"

}

buildTypes {

getByName("release") {

isMinifyEnabled = false

proguardFiles(

getDefaultProguardFile("proguard-android-optimize.txt"),

"proguard-rules.pro"

)

}

}

kotlinOptions {

val options = this as KotlinJvmOptions

options.jvmTarget = "1.8"

}

}

Does the above code look familiar to you? :). Yes, it's is something similar to what we do for app-level build.gradle. But it's not exactly that. It's DSL. The entire groovy code is represented by the DSL. Don't make the mistake of considering DSL some other language. It's still Kotlin. The syntax and way of writing produce much impact as if it's something else. We are going to cover how you can convert build.gradle into something like the above code in the upcoming chapter in detail.

Learn how to create DSL

In this section, we will understand how to create such syntax. How can you create your build.gradle. We are going to create a meal recipe application in upcoming chapters. So let's take the example of a Recipe instance here. What would you think, if Recipe object can be represented with the code as follows? Suppose Recipe object can be constructed like the following example:

val recipe = recipe {

id = 52942

recipeName = "Roast fennel and aubergine paella"

imageUrl = "https://www.themealdb.com/images/media/meals/1520081754.jpg"

cookingInstructions {

instruction { instruction = "1 Put the fennel, aubergine, pepper and courgette in a roasting tray" }

instruction { instruction = "Add a glug of olive oil, season with salt and pepper and toss around to coat the veggies in the oil." }

instruction { instruction = "Roast in the oven for 20 minutes, turning a couple of times until the veg are pretty much cooked through and turning golden." }

instruction { instruction = "Meanwhile, heat a paella pan or large frying pan over a low\u2013 medium heat and add a glug of olive oil. Saut\u00e9 the onion for 8\u201310 minutes until softened" }

instruction { instruction = "Increase the heat to medium and stir in the rice, paprika and saffron." }

instruction { instruction = "Cook for around 1 minute to start toasting the rice, then add the white wine." }

instruction { instruction = "Reduce by about half before stirring in two-thirds of the stock." }

instruction { instruction = "Reduce to a simmer and cook for 10 minutes without a lid, stirring a couple of times."}

instruction { instruction = "Pour over the remaining stock, arrange the lemon wedges on top and cover with a lid or some aluminium foil." }

instruction { instruction = "Cook for a further 10 minutes."}

}

}

The syntax is very readable, but it takes a good amount of effort to come up with such syntax.

The starting point is to think and visualize how your DSL will look like. This will give your idea of how you have to structure your code. It is also important that what you are trying to achieve by creating DSL. If something can be achieved using a simple class or data class, you should not create DSL for that. We have taken an example of object creation just for learning purposes. As you know DSL is all about passing functions as lambda and using class properties. While developing DSL you need to keep this in mind. Let's start now.

In the above code snippet, what it returns is a Recipe instance. Our recipe instance contains id, recipeName, imageUrl, and cookingInstructions properties. Achieving DSL for the basic data type is very easy. For example, id, recipeName, imageUrl can be represented by a class in the following way:

class Recipe(var id: Int = 0, var recipeName: String = "", var imageUrl: String = "")

Now let's create a function recipe(). The recipe() function will take a lambda argument and returns the Recipe type. The signature of the function is the following:

fun recipe(recipe: Recipe.() -> Unit): Recipe

So what does Recipe.() means? It indicates that the instance of Recipe will come as a parameter. It's passed as of this value as a receiver. Since instance is passed as a receiver, you can easily access all its properties and functions. The implementation of the recipe function is as follows:

fun recipe(recipe: Recipe.() -> Unit): Recipe {

return Recipe().apply {

recipe()

}

}

We call recipe() which is passed in parameter over the object of Recipe instance and return updated instance. We want to return an updated instance that's why we are using apply{} here. Easy peasy, lemon squeezy.

Now comes the most interesting and tricky part of DSL. The structure that we need to achieve for cookingInstructions is full of the lambda function, also it's of type List, not a basic data type like String or Int. How do we achieve this? Remember the basic definition of DSL, it is type-safe builders. This line is yelling and trying to tell you that it is influenced by the builder pattern. Use that concept. It's not the typical builder pattern we will be using, but somewhat similar.

Let's write a function in the Recipe class which we initially created. The function name will be cookingInstructions. This function is again a lambda function which takes the argument of type CookingInstructions type:

fun cookingInstructions(cookingInstructions: CookingInstructions.() -> Unit) {

this.cookingInstructions.addAll(CookingInstructions().apply(cookingInstructions))

}

Don't get confused between function names and class names. In DSL it's a convention to use the same function name for which you are trying to create the instance. The class CookingInstruction is a subtype of ArrayList<Instruction>. Why is that? Because we want to maintain a list of Instruction as data for class CookingInstruction. The CookingInstruction class is as follows:

class CookingInstructions : ArrayList<Instruction>() {

fun instruction(instructionBuilder: InstructionBuilder.() -> Unit) {

add(InstructionBuilder().apply(instructionBuilder).build())

}

}

The class CookingInstruction further has a function named instruction, which takes InstructionBuilder as a parameter. The InstructionBuilder has a responsibility to have actual instruction property. CookingInstruction just binds everything together and return the final list of instruction instances. Observe carefully the code for InstructionBuilder and type Instruction:

data class Instruction(var instruction: String = "")

class InstructionBuilder {

var instruction: String = ""

fun build(): Instruction = Instruction(instruction)

}

DSL can be great and easy in development once you can get rid of dealing with lambda functions and higher-order functions. We are going to create the whole layout of a screen in the upcoming chapter using DSL.

Bijou yet significant Kotlin features

We have seen that Kotlin has so much to offer. We cannot even try all features of a language in a single application even if we want to. In this section, we will learn features that are bijou but still have significant importance if you have a matching use case in your application.

Import alias

How many times you came across a situation where you have a conflict between imports because you and Android or any other library are sharing the same class name. I am sure you can remember 5 such instances without thinking too long. With the bulky APIs and usage of the open-source libraries, it is so common problem amongst developers. Classes' names like Account, Response, Request, Data, and Message are so common to find. See Figure 3.8:

Figure 3.8

Kotlin provides you to give an alias name to any import and you can avoid the conflicts for that class. The code can look like the following example:

import okhttp3.Request

import com.bumptech.glide.request.Request as GlideRequest

Now you can safely use Request and GlideRequest respectively without making your code look bad. You don't need to give an absolute package path for this now.

Naming companion object

We saw how companion object can be useful in the absence of static in Kotlin. We use companion objects to define statics sometimes. The companion object with the name can be something like the following:

companion object ArgumentParams{

const val RECIPE = "recipe"

const val FETCH_DETAILS = "fetch_details"

}

And the constants pr function defined in the companion object can be accessed as follows:

val bundle = bundleOf(RecipeDetailFragment.ArgumentParams.RECIPE to recipe)

But what benefit will it give to name a companion object? One can access all these constants and functions without naming the companion object. So why extra work? Well, companion objects in Kotlin are not just syntactic sugar. They actually type. They are able to do many more things, and need not be seen as the just replacement of static.

You can extend a class or implement an interface. See an example below:

open class Super {

open fun sayHello() {

println("Hello")

}

}

class Some {

companion object Child : Super() {

override fun sayHello() {

super.sayHello()

println("Hello from companion object")

}

}

}

fun main() {

Some.Child.sayHello()

}

Brilliant, isn't it?

Changing class name

As a Kotlin developer, we love using the top-level function. It is so easy and makes development faster. We just keep adding a bunch of functions under the same file and use it. But do you know from Java developer perspective it could be a nightmare. Imaging below is an extension function which your Java class wants to use. And the file name which you have given is StringExtension:

fun String.prefixHello(): String {

return "Hello $this"

}

So to use this in Java file, the developer has to use the class name StringExtensionKt. And full code can look like the following:

ChangingClassNameKt.prefixHello("Hello");

Poor looking code, isn't it? We did not respect Java's naming convention. Kotlin allows you to change the name of the generated class file using an annotation. In your StringExtension class file, you can use annotation @file. See the following code:

@file: JvmName("StringUtil")

package bijouKotlin

fun String.prefixHello(): String {

return "Hello $this"

}

Make sure @file annotation appears in the first line, before your package name. Provide an appropriate class name using the JvmName function. And miraculously a new class named StringUtil will appear in Java and you can access it as shown in the following snippet:

public static void main(String[] args) {

System.out.println(StringUtil.prefixHello("Hardik"));

}

Please note that if you use this @file annotation you will not see an autogenerated class i.e. StringExtensionKt. One more masterstroke from Kotlin for interoperability.

Wrapping up

Here we are coming to an end of learning Kotlin's some of the advanced concepts. We covered a lot of topics here, but lambdas and higher-order functions are topics that you will be using almost on a daily basis. I hope those curly braces followed by the function name now makes much more sense to you. Make sure you identify possible usage of lambdas if you are a senior developer or team lead and reviewing the code of your teammate. Collections and Generics are cool topics as well. Try to find the use case where you can use <T> and don't forget to restrict the operations using in and out.

Pop quiz

Once again it's a time for the quiz. This chapter was focusing on some of the advance concept of Kotlin. It's time to brush up the concept. Take a quiz and evaluate yourself. All questions are from the topic which we have learned so far in this chapter. There are total 10 questions and each questions has four options. Out of these four options only one is the correct answer. You need to choose that. I am sure you know all of them. Go crush them all. All the best!

What would be the return value of below function if the parameter data is null?

fun getLength(data: String?): Int {

return data?.length ?: 0

}

Returns 0

Throws NullPointerException

Compilation error

Returns null

If Any is the top most class in hierarchy, what is the bottom most class in Kotlin?

Unit

T

Void

Nothing

What will be the output of following function?

data class Person(val name: String?, val age: Int?, val address: String?)

fun main() {

showPerson(Person(name = "Hardik", age = 30, address = null))

}

fun showPerson(person: Person): Nothing {

println(person.name)

println(person.age)

println(person.address)

}

Prints…

Hardik

30

Null

Compilation error for showPerson

Prints…

Hardik

30

throws NullPointerException

Prints…

Hardik

30

What is isEmail in below code snippet?

val isEmail: String.() -> Boolean = {

this.matches("^[\\w-_\\.+]*[\\w-_\\.]\\@([\\w]+\\.)+[\\w]+[\\w]$".toRegex())

}

Boolean property

An extension function over type String.

Higher order function

Inline function.

What below code will do?

val list = List(5) { "Item -> ${it.plus(1)}" }

Compilation error

Initialize the list of size 5 of String, and prints Item -> 1

Initialize the list of size 5 of String type and assigns value "Item -> n" for each position.

Initialize the list of size 5 of String type and add value "Item -> 1" in list.

What is the difference between Sequence and Iterable?

Sequence is lazy while Iterable is eagerly initialized.

Iterable is lazy while Sequence are eagerly initialized.

Sequence is asynchronous and Iterable is synchronous.

None of the above are correct. They both are same.

If programmingLanguages is a list of String, what below code will print?

val programmingLanguages = listOf("Java", "Kotlin", "Python", "Go")

println(programmingLanguages.joinToString())

"Java", "Kotlin", "Python", "Go"

[Java, Kotlin, Python, Go]

Java Kotlin Python Go

Java, Kotlin, Python, Go

What is not true for Generics?

One of the benefit of using Generics is type safety.

You can apply one or more constraints to your Generics

Variance is a technique of defining relation between child object and parent objects.

Generics does not support extension function.

What is not true for following code snippet?

fun <T> copyLatestRecipeIntoAllRecipes(

allRecipeBook: RecipeBook<in T>,

latestRecipes: RecipeBook<out T>

)

It' is an example of Generics

This is also known as star * projection

It' is an example of using covariance and contravariance.

It' is an example of type projection

What following piece of code is doing?

@file: JvmName("StringUtil")

Creates an object in the file named StringUtil

Create the class name StringUtil instead of auto generated class name.

Open the file named StringUtil

Injects objects into StringUtil class.

Answers

Option A is the correct answer. The function getLength() will return 0 when parameter data is null. This is because function is using Elvis operator (?:) Elvis operator returns the value defined on it's right hand side if the object is null.

Correct answer is D. Nothing comes last in the hierarchy.

The function showPerson will not compile. The function's return type is Nothing. This means, the function cannot return anything but can throw Exception. For our implementation we are not throwing any exception so, the code will not compile. Though, you can return nullable vale if the return type is Nothing?

Correct answer is B. The signature of function says, String.() which makes it an extension function over type String. The above code will work perfectly fine for any String instance. The String instance can call isEmail(), for example, "loremispsum@gmail.com".isEmail()

Option C is the answer. The list is initialised using the concept of functional initializers. You can pass the lambda function and whatever that function returns that value will be assigned to all the positions. The type is automatically inferred, based on the return type of lambda function.

Correct answer is A. Sequence is lazy. This means it will only be triggered upon calling any of the terminal operator. On the other hand Iterable are eagerly initialized.

joinToString() is an extension function over type Iterable. By default it converts all items and appends comma (,) in between each item and convert all the records into String. You can easily change value of comma to anything you want. The separator parameter can be passed, for example, println(programmingLanguages.joinToString("#"))

Option D is not true for Generics. Generics can be applied on extension function as well.

Option B is not true. The example is using concept of use site covariance and contravariance. The star projection is used when you don't know the type or you don't care about type. List<*> is an example of star projection when type is not known.

Option B is correct answer. This annotation is used before the package declaration and value given inside double quotes is considered for the autogenerated class name. This autogenerated class name can be used while providing interoperability with Java.

CHAPTER 4

Design Patterns in Kotlin

“Design patterns are very useful when we study how they work so we can create similar patterns. They are bad when we try to copy them directly.”

- Anonymous

The above quotes say a lot when it comes to using design patterns. It tells both it's significant if understood and implemented correctly and at the same time it can lead to the disasters if the need is not identified and the wrong pattern is used. The design pattern is always a topic of discussion in interviews, conferences, podcasts, software design approaches. The topic was first introduced by four authors in their book titled Design Patterns: Elements of Reusable Object-Oriented Software. The authors were Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. They are famously known as the gang of four (GoF). It's more than a decade now they published their work but many topics are still relevant in software design.

Structure

This chapter covers the following topics:

Types of design patterns

Builder pattern

Singleton pattern

Decorator pattern

Facade pattern

Observer pattern

Chain of responsibility pattern

Objective

When GoF wrote this book and introduced this concept it was very much relevant and applicable to many languages. Nowadays programming languages are becoming modern. And there is always a debate that is this topic still relevant? For example, many design patterns do not make full sense when it comes to functional programming. Also, programming languages are coming up with such features that sometimes neglect the possible usage of design patterns.

There were 30+ patterns introduced in the book. All were mainly categorized into 3 categories:

Creational patterns: Provides the way to create an object or series of objects by hiding the object creation implementation.

Structural patterns: It establishes the relationship between the classes and objects. It highly uses inheritance and composition.

Behavioral patterns: This completely focuses on how objects communicate with each other.

Kotlin is a very modern language and aims to solve modern problems. There are some features introduced in Kotlin which changes completely the way you approach towards design patterns. In this chapter, we will be focusing on a very limited set of design patterns. Discussing those select design patterns makes complete sense as Kotlin makes difference here. We have selected 2 patterns per category and we will devote our time in this chapter to learn those patterns in Kotlin.

Builder pattern

Builder The builder pattern is a part of the creational pattern group. It is used to create complex objects. In Java, objects are created when the constructor is invoked. In some cases, object creation can be a complex procedure. By complex procedure I mean it could depend on a lot of scenarios and parameters, for example, manufacturing a car. A car depends on a lot of things, for example, engine, number of doors, color, name, transmission mode, braking system, etc. You can have a constructor that takes all the values as a parameter. But this will end up in a huge constructor with so many parameters. And you will always struggle to create such an object. The best approach here is to break down into small functions and use these functions to create an object. The builder pattern is all about combining this helper function and get the object created. Before you understand how Kotlin solves the problem of builder pattern you must understand how the builder pattern works.

Imagine you have to create a Car with below properties:

public class Car {

private String name;

private String colour;

private String engine;

private String breaks;

private String transmissionType;

private String carType;

}

The Car class has 6 properties and it can be more as well. I am sure you would not prefer a regular constructor with 6 parameters. Wouldn't it be great if we can pass only those parameters we need and all other parameters take some default value! Well, the builder pattern exactly does this. Let's see how we can do that. We will create an inner class called Builder. It's the convention to name such an inner class as Builder. For our Car class, a valid builder class name could be Builder or CarBuilder.

Note: It's not mandatory to name your class Builder or CarBuilder, but remember design patterns have some common meaning amongst the developer community. And being a developer you also have a responsibility to write a code which your peer or any community member can understand. Following a convention makes easy to interpret your code in the future or by any fellow developers.

A Builder class inside a Car class would be static. It cannot be non-static because non-static members, function or class can only be used by an object and we are using Builder class to create the object. A static class will have a setter type of a function and it will return its object (object of Builder class) as a result. Returning Builder class's object from any setter function is very crucial as this will allow the caller class or function to invoke another setter function. Take a look at the below static Builder class inside Car class:

public class Car {

//Your private member data

public static class Builder {

private String name;

private String colour;

private String engine;

private String breaks;

private String transmissionType;

private String carType;

public Builder setName(String name) {

this.name = name;

return this;

}

public Builder setColour(String colour) {

this.colour = colour;

return this;

}

public Builder setEngine(String engine) {

this.engine = engine;

return this;

}

public Builder setBreaks(String breaks) {

this.breaks = breaks;

return this;

}

public Builder setTransmissionType(String transmissionType) {

this.transmissionType = transmissionType;

return this;

}

public Builder setCarType(String carType) {

this.carType = carType;

return this;

}

}

}

Please notice that a Builder class will have the same set of member data as a Car class. Also, notice how each set function is returning the instance of Builder class using return this.

The next step is to create a build() function inside the Builder class. Every builder patter has a Builder type of class and every Builder type of class has build() function. Responsibility of build() function is to return the Car type of object. So the build() function would look as below snippet:

public Car build() {

return new Car(this);

}

As you can see build() is returning an instance of Car. But how does the constructor of Car looks like? We never created it so far. So in each correct builder pattern, you have to write a private constructor which is demonstrated below:

private Car(Builder builder) {

this.name = builder.name;

this.colour = builder.colour;

this.numberOfDoors = builder.numberOfDoors;

this.engine = builder.engine;

this.breaks = builder.breaks;

this.transmissionType = builder.transmissionType;

this.fuelType = builder.fuelType;

this.carType = builder.carType;

}

Ok, so we now know how to create the class which instantiates the object using the builder pattern. But how do we use that class? Below the main function demonstrates how you can use the builder pattern to create the object:

public class CarBuilderExample {

public static void main(String[] args) {

Car car = new Car.Builder()

.setName("Polo")

.setColour("White")

.setNumberOfDoors("4")

.setTransmissionType("Automatic")

.setFuelType("Petrol")

.setCarType("Hatchback")

.setEngine("1.2-litre TSI Petrol")

.build();

}

}

That's quite processed heavy patterns. To summarize builder patter below points you have to keep in mind:

The main class must have a private constructor.

The main class should have a static inner class.

The inner class will have all member data just like the main class.

The inner class will have public set functions that must return an instance of the inner class.

The inner class should have a function that returns the instance of the main class.

So this was the Java way to achieve the builder pattern. On the Kotlin side, it already has some powerful features like named arguments and default arguments which can help you to achieve what builder patter was doing. Kotlin also helps you to have a null safe builder without doing much. If you want to achieve the null safe builder pattern in the above Java code, you can imagine how many null checks your code will have. The more the parameter is, the more the checks will be and more causes the problems. In Kotlin let's see how Car class can look like below code:

class CarV1(private var name: String? = null, private var colour: String? = null,

private var numberOfDoors: String? = null, private var engine: String? = null,

private var breaks: String? = null, private var transmissionType: String? = null,

private var fuelType: String? = null, private var carType: String? = null

)

It is still loads of parameter at the time of creating a class. But at the time of calling it's not that painful as it feels in Java. Check out the below code:

fun main() {

val car = CarV1(name = "Polo", colour = "White", breaks = "ABS", carType = "Hatchback")

val petrolCar = CarV1(name = "Polo", colour = "White", breaks = "ABS", carType = "Hatchback", fuelType = "Petrol")

}

Look at this code. It looks so simple. And the effort is nothing in comparison to Java.

The disadvantage here with Kotlin is you do not get that feeling of creating a complex object step by step. Java's builder pattern code gives your idea about what is going on. I would say Java's builder pattern is much more readable compare to Kotlin. But Kotlin's code just works! If you are a die-hard lover of traditional builder patter, there is a way to achieve it in Kotlin as well. Refer to CarV2.kt inside designpatterns package.

A typical Kotlin style code using the default parameter and named argument kind of solve the problem by achieving immutability and less complex constructor.

Singleton pattern

If there is any design pattern in Java that is widely used, discussed, mis-used or loved by the developer community, then its singleton design patterns. Singleton design pattern makes sure that any point of time there remains only and only one instance in a system or memory, for a given class. It belongs to the creational pattern category. What are the scenarios where one would use the singleton design pattern? You would like to use a singleton design pattern probably for a single database instance, user-related information, dealing with some common configurations used across the system. Calendar class in Java is the best example of a singleton pattern.

Singleton pattern is although very straight forward to create. But it can go complex if you think of a multi-threaded environment. Let's see how to create a singleton class in it's simplest form. There are some key points which we need to keep in mind to create a class that will follow the singleton pattern. Below two points have to be taken care of. There are other points as well which we should keep in mind, but without these below-mentioned points you cannot achieve the singleton pattern:

Singleton class will always have a public static getInstance() method. This is always true for both a single-threaded or multi-threaded environment.

getInstance() method will create an object internally and return the instance.

Singleton class should have a private static member which refers to itself.

Also, they can only have a private constructor. Because public constructors will let developers create object anytime they want by just invoking it.

Take a look at the below code and which will give you an idea about how a simple singleton class can be created:

public class Database {

private static Database database;

private String name;

private Database(String name) {

this.name = name;

}

public static Database getInstance(String name) {

//Create the object only if it's null else return the already created existing object.

if (database == null) {

System.out.println("Creating an object");

database = new Database(name);

}

return database;

}

public String getName() {

return name;

}

}

You will notice that we have taken care of each and every point described earlier. And to use the Database class you would do something like below:

public class SingletonExample {

public static void main(String[] args) {

Database db1 = Database.getInstance("DB1");

System.out.println(db1.getName());

Database db2 = Database.getInstance("DB1");

System.out.println(db2.getName());

}

}

//Output

Creating an object

DB1

DB1

I am sure you have observed here that Creating an object is only printed once even if we have called getInstance() twice. This is because, during the second call, the database instance was not null. This is the simplest version of the singleton class is called lazy initialization.

Lazy initialization has some disadvantages when it comes to multi-threaded environments. Even if you check for null before creating an object there is a possibility that in the multi-threaded system there could be a possibility of two or more instances of the same class. To avoid this one could simply put a synchronized keyword against getting Instance() method, which results in below code:

public static synchronized Database getInstance(String name) {

//Create the object only if it's null else return the already created existing object.

if (database == null) {

System.out.println("Creating an object");

database = new Database(name);

}

return database;

}

But you will soon realize that this is not the solution to your problem. By marking getInstance() method as synchronized you are also preventing other threads to read your database instance because with synchronized thread only one thread can access the function at a time. This situation becomes worst and compels you to think of some other solution.

To address this problem you should use double-check locking methodology. In this method, the approach is to apply the lock only if objects are needed to create. So the lock will be applied inside the if block after the null check. And we will do the null check once again inside the synchronized block to make sure the object is null and then only create the object. The code looks something like below:

public static Database getInstance(String name) {

//Create the object only if it's null else return the already created existing object.

if (database == null) {

synchronized (Database.class) {

if (database == null) {

System.out.println("Creating an object");

database = new Database(name);

}

}

}

return database;

}

To achieve the maximum performance you also need to do one more modification ion the code, i.e. making the member data volatile which is as follows:

private static volatile Database database;

The volatile keyword helps you to achieve concurrency. It enables sharing the object between two threads and prevents the caching related issues.

Note: Disadvantage with double-checked locking is that only works above JVM 1.4. Also, it's too much verbose and complex in terms of readability.

The ultimate solution to this problem is finally solved by using enums. In Java 5 and above enums are by default thread-safe. Enums are very easy to implement. It is considered as the most concise and safest way to implement the singleton. Enums in Java can hold constants and methods inside. Using enum keyword you can define your data type.

Following code demonstrates our Database class as an enum to achieve the thread-safe singleton pattern:

public enum Database {

INSTANCE;

public String getName() {

return INSTANCE.name();

}

}

And to use this enum you do the following:

public class SingletonExample {

public static void main(String[] args) {

Database db1 = Database.INSTANCE;

System.out.println(db1.getName());

Database db2 = Database.INSTANCE;

System.out.println(db2.getName());

}

}

Oh but that's not the point. You don't want to know how to easily achieve a singleton pattern in Java, do you? You should demand what Kotlin has got here. How can you achieve a singleton pattern in Kotlin?

Well, Kotlin has got an object declaration for you. Object declaration looks like below:

object Database {

// Your properties

var name = "Database"

init {

println("Creating database")

}

// Your function

fun registerDataUpdateListener(listener: DataUpdateListener) {

}

}

Object declaration is by default singleton. So just with one keyword, you get a singleton pattern with thread safety. What more you need?

Object declarations are initialized lazily. Also, one more thing to keep in mind is that the object declaration is not an expression. That means you cannot use them on the right hand side of the assignment (=) operator. They do not return any value. You just use them statically. Object declaration cannot be extended.

So how do you use object declarations? Follow the example mentioned here:

fun main() {

println(Database.name)

println(Database.name)

}

//Output

Creating database

Database

Database

Here init will only be called once. The println() statement inside init block is only printed once and that proves that Database is lazily initialized.

Decorator pattern

The next pattern we are going to study is called the decorator pattern. As the name suggests it decorates the class without modifying its structure. Many times you deal with legacy code or try to add very little functionality to existing classes. An inheritance is not always a solution. The class which you want to modify could be final. The class which you want to modify could affect a lot of other flows. So how do you enhance the functionality of that class? In such a situation you should use the decorator pattern.

Imagine you have various food making machine which produces food like French fries and sandwich etc. Now you want to add garnishing feature on top of it. Here you do not want to modify the core classes which is helping us to generate such food items. You try to wrap the core classes under the name of some class. The new wrapper class will have the responsibility of doing garnishing on your food.

If you want to achieve a decorator pattern in Java you end up making a lot of interfaces and you heavily use inheritance. Let's say you have below interfaces and classes:

public interface Recipe {

void cook();

}

public class FrenchFries implements Recipe {

@Override

public void cook() {

System.out.println("Cooking french fried");

}

}

public class Sandwich implements Recipe {

@Override

public void cook() {

System.out.println("Preparing recipe");

}

}

So far the system is clear. Whatever food classes you create they all implement recipe interface and all food implement cook() method. There can be such multiple classes in the system. Now as we mentioned before, how do you provide garnishing in this system. There can be a possibility that not all food classes want to garnish. Ideally, you should create a decorator class which supports garnishing for all Recipe type of instance. The RecipeDecorator class will look something like below:

public abstract class RecipeDecorator implements Recipe {

protected Recipe recipe;

public RecipeDecorator(Recipe recipe) {

this.recipe = recipe;

}

@Override

public void cook() {

recipe.cook();

}

}

RecipeDecorator holds the Recipe instance internally and override cook() method and calls the instance's cook() method internally. Now, whatever type of decorator you want to provide that all classes will implement RecipeDecorator. RecipeDecorator is an abstract class. Following is the CheeseDecorator which will provide implementation to RecipeDecorator classes:

public class CheeseDecorator extends RecipeDecorator {

public CheeseDecorator(Recipe recipe) {

super(recipe);

}

@Override

public void cook() {

super.cook();

garnishCheese();

}

private void garnishCheese() {

System.out.println("Keep calm and shred cheese…");

}

}

You can use this CheeseDecorator classes in the following way:

Sandwich sandwich = new Sandwich();

CheeseDecorator cheeseSandwich = new CheeseDecorator(sandwich);

sandwich.cook();

cheeseSandwich.cook();

But when it comes to Kotlin, you can leverage the delegation feature. Delegation is a proven way to achieve inheritance. Take a look at the below code and see how Kotlin can help you to achieve the decoration pattern:

interface Recipe {

fun cook()

}

class Sandwich : Recipe {

override fun cook() {

println("Preparing sandwich")

}

}

class CheeseDecorator(val recipe: Recipe) : Recipe by recipe {

override fun cook() {

recipe.cook()

garnish()

}

private fun garnish() {

println("Keep calm and shred cheese…")

}

}

fun main() {

val sandwich = Sandwich().apply { cook() }

val cheeseSandwich = CheeseDecorator(sandwich).apply { cook() }

}

For CheeseDecorator class we delegate recipe instance and override the functions. Pretty simple and readable code, isn't it? With the keyword by you can achieve delegation as we have already learned in the previous chapter's Delegated properties section.

Facade pattern

The next pattern on the list is the Facade pattern. Facade in simple meaning is front of any building which opens to street or some open ground. The purpose of having a facade is to hide the complexity of a lot of other small systems. In software to achieve a Facade pattern, you do exactly this by hiding complex implementation and providing an easy interface to deal with it. Let's take the example of an Operating system. The user interface is a perfect and practical example of a facade pattern. It hides all the complexity of various other systems. Imagine a small operating system (OS) that has only three programs in it. Music player, network service and document manager. You can represent all three systems in a simple class like below:

class MusicPlayer {

void playMusic() {

System.out.println("La la la la la….");

}

}

class NetworkService {

void browseServer() {

System.out.println("Loading data from https://bpbonline.com…");

}

}

class DocMaker {

void makeDocuments() {

System.out.println("Creating a document…");

}

}

To deal with these three classes or programs, let's create another class called UserInterface:

class UserInterfaceFacade {

private MusicPlayer musicPlayer;

private NetworkService networkService;

private DocMaker docMaker;

UserInterfaceFacade() {

musicPlayer = new MusicPlayer();

networkService = new NetworkService();

docMaker = new DocMaker();

}

void playMusic() {

musicPlayer.playMusic();

}

void browseInternet() {

networkService.browseServer();

}

void manageDocs() {

docMaker.makeDocuments();

}

}

The UserInterface creates an object of these three classes and provide public function like playMusic(), browseInternet(), and manageDocs(). And as a user, you just call these functions without worrying about what's happening behind the scene. See the demo in below code snippet:

public class OperatingSystem {

public static void main(String[] args) {

UserInterfaceFacade userInterface = new UserInterfaceFacade();

userInterface.browseInternet();

userInterface.playMusic();

userInterface.manageDocs();

}

}

You usually initialize all the instances that the Facade pattern uses inside the constructor. You can slightly optimize the code by initializing the object whenever needed. Blindly creating the instance inside the constructor creates unnecessary objects before it's usage. We will not solve that problem here, though.

Kotlin doesn't provide any out of the box support like singleton or builder pattern here. But like delegation, you can use the default parameter feature here to reduce your code. You should create a UserInterface class something like below:

class UserInterface(

private val musicPlayer: MusicPlayer = MusicPlayer(),

private val networkService: NetworkService = NetworkService(),

private val docMaker: DocMaker = DocMaker()

{

fun playMusic() {

musicPlayer.playMusic()

}

fun browseInternet() {

networkService.browseServer()

}

fun manageDocs() {

docMaker.makeDocuments()

}

}

}

If your classes have a complex parameterized constructor, you should rely on init{} block to instantiate all the objects.

Observer pattern

So far we studied how Kotlin's syntax or features helps us to achieve widely used design patterns. In this section, we will take a look at the one more widely used behavioral pattern known as Observer. The fundamental of reactive programming is observing a change. And the moment you notice the change react on it. That's the core philosophy. You can observe value change in the variable/object. You can observe any event change. You can observe for a function to return some value. Possibility and usage can be numerous.

To understand the observer pattern well let's take the example of a text value change. In this example, we will observe the change in value for a String instance. When the value is changed we will notify all observers with its old and new value. We will use the interface to communicate with multiple observers. Whatever we want to observe is called a subject. Below is the class which has a subject called text? The class also holds a list of ValueObserver. And ValueObserver is nothing but a simple interface with notifyChange() method:

public interface ValueObserver {

void notifyChange(String oldText, String newText);

}

public class TextInput {

private List<ValueObserver> observerList = new ArrayList<>();

private String text = "";

void addObserver(ValueObserver observer) {

observerList.add(observer);

}

public String getText() {

return text;

}

void setText(String text) {

String oldText = this.text;

this.text = text;

for (ValueObserver observer : observerList) {

observer.notifyChange(oldText, this.text);

}

}

}

Now let's create two observers and call them ObserverOne and ObserverTwo. These two classes will implement ValueObserver interfaces and provide implementation to notifyChange() method:

public class ObserverOne implements ValueObserver {

@Override

public void notifyChange(String oldText, String newText) {

System.out.println("Observer 1 notified. Value changed from " + oldText + " to " + newText);

}

}

public class ObserverTwo implements ValueObserver {

@Override

public void notifyChange(String oldText, String newText) {

System.out.println("Observer 2 notified. Value changed from " + oldText + " to " + newText);

}

}

Now let's connect the dots. We need to have a class that creates an instance of TextInput and adds observables in it. The below class does that. Also, we are changing the value of the text to trigger the event which will eventually call notifyChange() for both the observer classes:

public class ObservableDemo {

public static void main(String[] args) {

TextInput textInput = new TextInput();

textInput.addObserver(new ObserverOne());

textInput.addObserver(new ObserverTwo());

textInput.setText("Hello");

textInput.setText("World");

}

}

That was pretty much simple and straight forward. What Kotlin has to offer for such a simple thing? Can this be even easier than this? The answer is Yes. Try to think about some learning we had in Chapter 3: Go to the Depth of Kotlin. We learned delegated properties. Within delegated properties, there were delegated observables. We can use this feature as an observable pattern. What we need here is to get notify whenever the value is changed. Delegated observable exactly does the same.

Let's see how! A TextInput class now contains an observable property as shown in the below code:

class TextInput(valueObserver: (String, String) -> Unit) {

var text: String by Delegates.observable("") { prop, old, new ->

valueObserver.invoke(old, new)

}

}

The constructor will take a function which has two String parameter and returns nothing. We can define that function as a lambda which looks like the following code:

val valueObserver: (String, String) -> Unit =

{ oldText, newText -> println("Observer notified. Value changed from $oldText to $newText")

}

And inside the main function, we will simply create the object and try to update the value to generate the value change event:

fun main() {

val textInput = TextInput(valueObserver)

textInput.run {

text = "Hello"

text = "World"

}

}

The whole interface verbosity is just gone by using Delegates.observable only. And code makes much more sense now. You can easily read it.

Chain of responsibility pattern

This is the last pattern we have included in our list of design patterns. This pattern is known as a chain of responsibility and it's part of the behavioral pattern category. Assume you have a set of rules to apply to given data. The rules can be complex and may require a good amount of logic. You also want to abstract the rules and provide an easy single interface to trigger your rule check. And as a developer, you also want to follow a single responsibility principle. In this situation, you can use a chain of responsibility pattern as its name suggest. You can decouple such a request and each layer will have some responsibility. And they together contribute to some common results.

Let's take the example of an ATM. ATM accepts input from a user and dispenses some money. Now behind the scene, it calculates what number of which currency value should be returned to the user. Based on the amount you have entered it calculates the currency notes and dispenses it to you. This can be done using a chain of responsibility pattern. Assume you have currency notes of £10, £20 and £50 denomination. And if the user asks for £60 you would like to return one note of £50 and one of £10.

There would be an interface that will provide the function to handle the scenario and also an instance of its next rule. The interface looks like the following:

interface CurrencyDispenser {

val currencyValue: Int

val nextDispenser: CurrencyDispenser?

fun dispenseMoney(amount: Int)

}

We have taken an extra variable named currencyValue for some other calculation. This will represent default value for each of the currency denomination. Next, we will create classes that will implement CurrencyDispenser interface and provide implementation to it:

class DispenseTen( override val nextDispenser: CurrencyDispenser? = null, override val currencyValue: Int = 10) : CurrencyDispenser {

override fun dispenseMoney(amount: Int) {

// Implementation details

}

}

class DispenseTwenty(override val nextDispenser: CurrencyDispenser? = null, override val currencyValue: Int = 20) : CurrencyDispenser {

override fun dispenseMoney(amount: Int) {

// Implementation details

}

}

class DispenseFifty(override val nextDispenser: CurrencyDispenser? = null, override val currencyValue: Int = 50) : CurrencyDispenser {

override fun dispenseMoney(amount: Int) {

// Implementation details

}

}

In the above three classes, we have override currencyValue and provided its value. Similarly, nextDispenser should hold the instance of the next chained class. If it's null that means it's an end of a chain. The implementation detail of dispenseMoney() for all the classes could be different. That depends on what is the responsibility of each class and how it will be performed.

After this, we should create a separate class to which will create the chain. It could be a class or a function. In our example, we have created a class called ATM:

class ATM {

private val ten = DispenseTen()

private val twenty = DispenseTwenty(ten)

private val fifty = DispenseFifty(twenty)

fun withdrawMoney(amount: Int) {

fifty.dispenseMoney(amount)

println("Denominations for $amount")

}

}

The responsibility of the ATM class is to create the chain and trigger the first action. ATM class will be responsible to establish a communication with the client object. ATM class can be called as demonstrated below:

fun main() {

val atm=ATM()

atm.withdrawMoney(570)

}

After we call atm. withdrawMoney() the chain will be active and all the required blocks of the chain will be processed internally. Having a separate function or class for each responsibility is helpful in such kind of situations. It helps with testing and debugging a lot. Also, it keeps your code segregated and clean.

Wrapping up

Knowing design patterns will surely help to write quality code. We now know how some problems which are common to all developers can be solved using this proven design patterns by GoF. Many design patterns are still relevant. A lot of frameworks are using these patterns. With the help of Kotlin's feature and its capability to write create a concise syntax makes code beautiful and readable.

We saw how exceptionally well code can be written for Singleton and Builder design pattern. Similarly, for other design patterns like observer and decorator, Kotlin does save a lot of time with its syntax and features like delegated observable.

There are also a few more design patterns like Factory, Adapter, State, Strategy which can be implemented in a nice manner using Kotlin's feature. That would be a nice self-learning and I am sure you would enjoy it.

CHAPTER 5

Analyzing and Architecting a Meal Recipe App

Design is not just what it looks like and feels like. Design is how it works.

- Steve Jobs

Readers, we have just reached a milestone. We finished the learning of Kotlin. At this moment we have all the required knowledge for Kotlin as far as language is concerned. We know language up to a point where we can at least code. From this chapter onwards it is assumed that you have the basic idea about Android application development.

Structure

This chapter covers

Understand the requirements for a meal recipe app

Develop user interfaces

Understanding architecture components with Kotlin

Setting up dependency injection framework Koin

This chapter and all coming chapters are now focused on getting Android application ready. Whatever we have learned in previous chapters, we will put it in practice. We will no longer use IntelliJ IDEA to write Kotlin code. Android application is developed on a completely separate IDE called Android Studio. We need a working setup for Android Studio. Before you read further please make sure you have below points ready and working for you:

Downloading Android Studio: https://developer.android.com/studio

Installing Android Studio: https://developer.android.com/studio/install

Creating a sample Android project in Kotlin: https://developer.android.com/studio/projects/create-project

Running the application on the emulator: https://developer.android.com/studio/run/emulator

Above all links will take you to the official website for the developer. There each and everything is explained in a step by step manner. Also, there are videos provided for the installation process. You should not face any trouble performing any of the steps.

Also in the first chapter, we have already created an Android studio project named The international dhaba. We will go back to that project now and develop that application.

Now before we start writing code and developing our Android application, we need to understand what we are building. Let's put everything aside and try to understand the requirement of the application which we will be building. We want to develop a meal recipe app by using our Kotlin knowledge and learnings from previous chapters. We will use all the features of Kotlin what we learned before. We also aim to learn something new and useful topics in parallel. The new topics which we will learn are very much used by Android's developer community. They are considered as the best tools and part of many application's technical stacks.

Understand the requirements for a meal recipe app

As a part of learning, we will prepare an Android application. And the Android application will be showing meal recipes. The meal recipe application is an abstract term. I can contain a lot of features. In this section, we will try to visualize our meal recipe application and try to finalize the requirements. We will have very basic features. Assume our product owner (PO) has a below requirements for us:

As a user, I want an Android application where I can browse through various meal recipes.

As a user, I want to see a list of meals with its ingredients.

As a user, I should be able to see steps to prepare that meal.

As a user, I should be able to see the latest recipe.

As a user, I should be able to search for a meal by its name.

As a user I should be able to see meal by category, for example, Vegan, Seafood, Chicken, etc.

Once we got the requirement, we have asked our PO a few questions and we have got answers as well from PO. They are like the following.

We asked:

Should we display an image for each recipe? Answer: Yes

Should we display images for each ingredient? Answer: No

Should we store meal recipe data in a local database or data that comes from a remote database? Answer: Data should load from the remote database

Our PO expects we follow the latest trends and technology in the market and want us to use Kotlin as a programming language. PO insists that we make sure our code is of production quality and we follow all the best practices we know.

With this brief, the project kicked off.

At this point, we as a developer will always start creating a mind map. If you have worked or working in an agile team, you may go for story mapping sessions with your team. I always like story mapping session. I like to do it with my team. This allows team members to agree. This makes development easy in the future. Because everyone has the same vision. Also, everyone will have an idea about how the team will be making progress to achieve the goal. You should have your business analyst (a person who has idea about requirements. BA gets requirements from PO), Designers, Developers, QA. And ideally scrum master facilitates this session. Let's understand a bit more about user story mapping.

User story mapping

Jeff Patton originally introduced this wonderful concept known as user story mapping. The intention of this session should be visualizing your backlog, come up with a QA strategy, preparing a release plan, and if possible come up with epic or high-level stories. It gives an idea about the timeline, user activities, and tasks, stories, priorities, and releases. In the end, you have the exact sequence of user activities, your PO will give you priorities so tasks will be in a specific order. You get a rough idea about the timeline. And your release manager will have an idea about what are the different phases of the release of the product. I have created a user story mapping as described in Figure 5.1:

Figure 5.1

Once we have defined the user stories, the next task for us is to define how part? How we will display a list of meal recipes? How the details will look like? How the search would work? How we display application name and splash image while the application is loading? How we will share the required details using other applications? Some of these questions are for UI/UX designers but there are also some questions that we have to technically answer. For example, how we will load data from remote? How to perform animations? How to lazily load images? What type of architecture will suit our application? We will find out answers to all these questions.

Developing user interface

When it comes to defining a user interface, one should think about it as an art. Because the user interface is something you user is going to see first. That creates the first impression in your user's mind. It has to be intuitive and appealing. There is a lot of philosophy and thoughts around this concept. UI/UX is a very key ingredient of a successful application. In industry, there will be a specialist's team whose sole duty is to create a beautiful, yet simple and effective user experience and user interface. This team will help you by providing themes, wireframes, and references of each and every screen which will be visible to your end-user. Take a look at the Figure 5.2

Figure 5.2

Creating layouts

Each screen in your application is called layout in Android. The layout can be a group of Views or ViewGroups in Android. ViewGroup, as name suggests, is nothing but a collection of subclasses of type View. The most convenient way to create layouts in Android is by using an XML. Every Android resource has a layout directory. All the layout will be created inside /layout directory. Android Studio has in-built support for designing your layouts.

To create a layout in Android Studio:

Click on your Android project's app directory in your project window.

From the menu select File | New/XML | Layout XML File.

You can also right-click on res directory and select New from the menu. Select the Android resource file. A dialog box will open and within that dialog, select layout as a Resource type.

If you have selected an already created layout, then based on your layout design, a layout editor in Android will look like below:

Figure 5.3

There are numerous types of ViewGroup types, for example, LinearLayout, RelativeLayout, ScrollView, CoordinatorLayout, AppBarLayout, ConstraintLayout, etc. We have a widely used constraint layout in our application. ConstraintLayout offers a highly optimized layout.

ConstraintLayout

Layouts designed using ConstraintLayout are rendered fast on the surface. ConstraintLayout is using the concept of a flat hierarchy. Each of the views is positioned based on a few criteria. They are relatively positioned on the surface. You can create large and complex layouts very easily using ConstraintLayout and layout editor in Android Studio. ConstraintLayout is responsive that means if designed properly it adjust automatically based on the device's dimensions and type. It also adjusts automatically for tablet type of devices. Figure 5.3 is a glimpse of the recipe_detail_fragment.xml which is developed using ConstraintLayout. To place any view properly in the ConstraintLayout is to make sure the view has at least three constraints. The view should ideally have left and right constraints. And it should also have either top or bottom constraint. This will make sure the layout is rendered properly on any type of dimensions and screen size. There are cases where you can also set only two out of four constraints to put the layout properly on the surface. ConstraintLayout also has little complex concepts like barriers, chains, guidelines, and groups. This is used when the layout is a little bit complex. Since we are developing a meal recipe app, I would say ConstraintLayout can be a secret ingredient of your perfect looking and highly optimized layouts.

Below is extracted code which generates layout for your category list item:

<?xml version="1.0" encoding="utf-8"?>

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http://schemas.android.com/apk/res-auto"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="wrap_content">

<ImageView

android:id="@+id/categoryImageView"

android:layout_width="0dp"

android:layout_height="@dimen/imageBigHeight"

app:layout_constraintBottom_toBottomOf="parent"

app:layout_constraintEnd_toEndOf="parent"

app:layout_constraintHorizontal_bias="0.0"

app:layout_constraintStart_toStartOf="parent"

app:layout_constraintTop_toTopOf="parent"

app:layout_constraintVertical_bias="0.0"

app:srcCompat="@color/colorAccent" />

<TextView

android:id="@+id/categoryNameTextView"

android:layout_width="0dp"

android:layout_height="wrap_content"

android:layout_marginStart="@dimen/eight"

android:layout_marginTop="@dimen/eight"

android:layout_marginEnd="@dimen/eight"

android:gravity="center"

android:textAppearance="@style/TextAppearance.AppCompat.Large"

android:textStyle="bold"

app:layout_constraintEnd_toEndOf="parent"

app:layout_constraintHorizontal_bias="1.0"

app:layout_constraintStart_toStartOf="parent"

app:layout_constraintTop_toBottomOf="@+id/categoryImageView"

tools:text="Recipe Name" />

</androidx.constraintlayout.widget.ConstraintLayout>

You can learn more about ConstraintLayout here https://developer.android.com/training/constraint-layout

But you might be thinking, that this book is all about Kotlin, why are we not talking about Kotlin and why we are discussing ConstraintLayout? So let's switch the track back to Kotlin.

Creating layout using DSL

So here comes the Kotlin part. Kotlin offers this marvelous concept called DSL. DSL stands for Domain Specific Language. DSL allows you to handle huge data which is hierarchical. By naming functions and classes in the best way it's easy to create such builder sort of functions in a semi declarative way.

Below code will give you a glimpse of how DSL in Android can look like:

verticalLayout {

val userName = editText()

button("Login") {

onClick { toast("Hello, ${userName.text}") }

}

}

You will soon realize that the above piece of code is used to generate the layout. Under the hood, verticalLayout, editText, button, onClick is nothing but a function that takes lambda arguments. The DSL to generate layout is not part of standard Kotlin API, you have to use the external library to use this feature. Anko is the project which offers some great utilities for Kotlin, specially designed for Android. You can know more about Anko here https://github.com/Kotlin/anko. We will also cover Anko in these books into the coming sections.

Now as a part of learning DSL let's create a layout in it for our app. As we understood, DSL is largely used in generating layout in Android. We are going to develop a layout for SplashActivity. At the end of our development and learning exercise, a SplashActivity will look like below. This will help you to visualize the layout while we code:

Figure 5.4

So by looking at the image, we should know what all elements will be required. Try to create a mind map about how you will arrange your layout. We have one ImageView or View to display background image and three TextView (One each for the app name, application marketing tagline, and credit for image source). And also there will be one ViewGroup which will serve as a container. ViewGroup will be ConstraintLayout.

Let's add Anko as a dependency on project. Use below code and paste it inside you app level build.gradle:

implementation "org.jetbrains.anko:anko:0.10.8"

implementation "org.jetbrains.anko:anko-constraint-layout:0.10.8"

And sync project with your .gradle file. Once the dependency is installed we are ready to code our first DSL. Create a basic Activity name SplashActivity. After the basic Activity is created you will have to override onCreate function to satisfy the need of compiler. onCreate is mandatory is overridden, it's a crucial part of lifecycle callback where you get notified whenever Activity is created by OS and ready to function. Create a private function within SplashActivity name it generateSplashView(). Paste below code in that newly created function:

private fun generateSplashView() {

background = ContextCompat.getDrawable(this@SplashActivity, R.drawable.splash_bg)

constraintLayout {

val blackBgImage = view {

id = R.id.blackBg

setBackgroundColor(ContextCompat.getColor(this@SplashActivity, R.color.transparentBlack))

}

val appNameTextView = textView {

id = R.id.appNameTextView

text = getString(R.string.display_name)

}

val taglineTextView = textView {

id = R.id.taglineTextView

text = getString(R.string.tag_line)

}

val imgCreditTextView = textView {

id = R.id.imgCreditTextView

text = getString(R.string.image_credit)

}

}

}

You might have to adjust some imports, if auto import is not on in your IDE. Import required packages from org.jetbrains.anko. As you see the code is full of blocks. And these blocks are nothing but lambda functions. They are named as if they are view instances. I am sure you are a curious mind so let's go one level down. If you see the code behind textView function it's something like below:

inline fun ViewManager.textView(init: (@AnkoViewDslMarker android.widget.TextView).() -> Unit): android.widget.TextView {

return ankoView(‘$$Anko$Factories$Sdk27View’.TEXT_VIEW, theme = 0) { init() }

}

The signature of textView function says it's an inline extension function written for class ViewManager. ViewManager is an interface that is implemented by all ViewGroup and View subclasses. Also, observe the parameters of the textView function. The function name is init and takes any parameter which is an instance of TextView, that is, id, text, textColor, etc. and it returns Unit that means no return value is there. It will just call init function inside. ankoView is another wrapper function that behaves like a factory of generating a view and adding it into view hierarchy. Ok so now we have a decent idea what's happening under the hood of those functions. Anko is also an open-source project, you can visit the repo which I have already shared and satisfied you're hungrier mind.

So far the DSL which we have created is adding some view as a child of ConstraintLayout but it doesn't have any parameters and styling. It won't get the appropriate position when the view will be drawn. So now let's work on it. We will set correct layout params and constraints now as our next task.

To set the constraint to each view, we will use DSL defined as constraintSet. There is also applyConstraintSet which holds instances of constraintSet and together they create a complete set of constraints and apply it to views. Let's try to apply constraints for our view named appNameTextView. We need that view to be in the center. Below code will set that constraint:

applyConstraintSet{

appNameTextView {

connect(

START to START of PARENT_ID,

TOP to TOP of PARENT_ID,

END to END of PARENT_ID,

BOTTOM to BOTTOM of PARENT_ID

)

}

}

The applyConstraintSet function would be still part of constraintLayout block. There is also various way to apply the configuration on each view. You can either refer it using the instance of the view which is created. Above is an example where we use instance name i.e. appNameTextView to set constraints. From the code, you can see that we are applying all four constraints. But if you take a careful look, some of the Kotlin features are used to generate this super readable code. Those features are as follow:

START to START of PARENT_ID

Here START, TOP, PARENT_ID, END, BOTTOM are nothing but enum class instances. They are called Side.

To infix function is used to generate a Pair.

On the instance of Pair, another infix and extension function is written named off.

Of function written the instance of BasicConnection, which internally creates the constraint using the Side.

The readability is all due to these super-duper syntactic sugar. Infix function and extension function we have studied in depth in Chapter 2: Kotlin Fundamentals. Here they are used in practice.

You can also configure constraints by view id. The view hierarchy we have created has id for each and every view. This time let's apply the constraint to imgCreditTextView. imgCreditTextView should be at the bottom of the screen and text should be right-aligned. Paste below code in your applyConstraintSet block to set constraints for imgCreditTextView:

R.id.imgCreditTextView {

connect(

START to START of PARENT_ID,

END to END of PARENT_ID margin dip(4),

BOTTOM to BOTTOM of PARENT_ID margin dip(4)

)

horizontalBias = 1.0f

}

This will put the imgCreditTextView on the appropriate position. Apart from using typical connect function, you can also use a lot of other useful functions like margin, dip. There are also a few properties you can use, for example, we have used horizontalBias. Similarly, these DSL also offers few more properties like horizontalBias, visibility, defaultWidth.

Below code will complete the generateSplashView function:

private fun generateSplashView(): ConstraintLayout {

return constraintLayout {

//Code from above-shared snippet to generate the view hierarchy.

applyConstraintSet {

blackBgImage {

connect(

START to START of PARENT_ID, TOP to TOP of PARENT_ID,

END to END of PARENT_ID, BOTTOM to BOTTOM of PARENT_ID

)

}

appNameTextView {

connect(

START to START of PARENT_ID, TOP to TOP of PARENT_ID,

END to END of PARENT_ID, BOTTOM to BOTTOM of PARENT_ID

)

}

R.id.taglineTextView {

connect(

START to START of PARENT_ID, END to END of PARENT_ID,

TOP to BOTTOM of R.id.appNameTextView margin dip(8)

)

}

R.id.imgCreditTextView {

connect(

START to START of PARENT_ID, END to END of PARENT_ID margin dip(4),

BOTTOM to BOTTOM of PARENT_ID margin dip(4)

)

horizontalBias = 1.0f

}

}

}

}

Let's now run the activity and see how our layout looks. Make sure you have declared your Activity in your AndroidManifest.xml file. Without this, you cannot see/start your Activity. By not defining your Activity in AndroidManifest.xml file you will see a runtime exception named ActivityNotFoundException:

com.hardiktrivedi.theinternationaldhaba E/AndroidRuntime: FATAL EXCEPTION: main

Process: com.hardiktrivedi.theinternationaldhaba, PID: 4004

android.content.ActivityNotFoundException: Unable to find explicit activity class {

com.hardiktrivedi.theinternationaldhaba/com.hardiktrivedi.theinternationaldhaba.view.SplashActivity

};

Have you declared this activity in your AndroidManifest.xml?

To avoid the above except you should define each and every activity in your AndroidManifest.xml file as follows:

<activity

android:name=".view.SplashActivity"

android:theme="@style/Theme.AppCompat.Light.NoActionBar.FullScreen">

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity

You may omit the attributes like intent-filter or theme etc. But your <activitiy…> tag must have a name attribute. The name should match with your Activity class.

Note: Android Studio is very smart. If you create Activity using its wizard, it will automatically make an entry into the manifest file. Also, it's just that at least one activity from manifest file holds the intent-filter tag which has acted as android.intent.action.MAIN and category as android.intent.category.LAUNCHER. By not doing so, you will usually get lint error and your application will not build.

We did good work around this layout creation using DSL. Below are a few observations I made while working on DSL which I have jotted down its merits and demerits.

The following are the merits of Anko DSL while creating layouts:

DSL is all about type safety. You get no run time surprises.

Highly readable code compare to XML layouts.

You get null safety by default while you work with DSL.

You can reuse some of the code. Where in XML there is very little probability to reuse the code.

The following are the demerits of Anko DSL:

You do not get a warning when you miss critical parameters like constraint values.

You do not have the privileged to see the preview of the layout. Have to run the app every time to see the output.

Not out of the box support with R class. For drawable, string values and color you have to use Android API to get the correct compatible values.

It requires a bit more learning. As for Android developers, it's not easy to switch from XML to dynamic code generation style.

Anko DSL for layout does not have attributes like textStyle and lineSpacingExtra.

For this chapter we have only created the layout for SplashActivity using DSL, other layouts are still developed with the traditional approach using XML only. But they all use ConstraintLayout.

Theming and styling

Each and every good application will have a theme and style. They both together give a great uniformity between various view elements on the screen or window. In Android, the layouts or user interface is developed in a unique way. You develop your user interface as an XML file. And that XML file is inflated and converted into View classes after the project is built. The whole idea of developing UI is to keep the UI layer separate from your business logic. Now view can be stylized and customized. This customization is also done pretty much using the XML only. This is a very powerful approach and Android development IDE i.e. Android Studio greatly supports this. There is exclusive support especially for the Android platform from JetBrains.

Styles

Style is nothing but a set of attributes applied to a View. A View is anything that can be drawn on the surface. For View attributes like background color, text size, text color, text styles, for example, bold, italic can be considered as style attributes. A style can combine all these attributes and hold them under one name. The style in Android is considered as a resource and you can define your styles under res/values directory. Android developers usually name them as styles.xml.

Let's take a simple example and take a look at the below code:

<style name="TextMedium">

<item name="android:textSize">12sp/item>

<item name="android:textColor">#FFFFFF</item>

</style>

The code snippet defines the style named TextMedium. This style defines the value of two attributes which can be applied to any View which has text property. These views can be TextView, EditText, or Button. And you can apply that style to TextView as demonstrated below:

<TextView style="@style/TextMedium" … />

This opens the door of a lot of other possibilities. You get the benefit of reusability and scalability. You can easily apply any particular style to as many views as you want. In complex and big applications this approach helps a lot. This is considered a best practice in Android. You can easily extend any style. Below example explains how style can be extended and modified as per your requirement:

In Android there is already a very well defined set of styles, one of the styles is to display headline sort of content, which is defined like:

<style name="Base.TextAppearance.AppCompat.Headline">

item name="android:textSize">@dimen/abc_text_size_headline_material</item>

<item name="android:textColor">?android:textColorPrimary</item>

</style>

The value of textSize is 24sp and the value of textColor is whatever you have defined as your primary color. What if, you do not want textSize as 24sp. This is a clear case of inheritance, where you want to use everything that is offered except a few customizations. You can easily extend the style Headline give a new name and use it. You can inherit and modify the style as follows:

<style name="AppName" parent="TextAppearance.AppCompat.Headline">

<item name="android:textColor">@color/white</item>

<item name="android:textSize">40sp</item>

<item name="android:lineSpacingExtra">20sp</item>

<item name="android:gravity">center</item>

</style>

Not just you modify the textColor and textSize but you also add a few more attributes to the styles, for example, gravity and layout_width or layout_height, etc.

Since styles are part of resources and defined inside res directory. Resources in Android can be customized for locale and API level as well. For example, you can customize style for your English speaking customers and Spanish speaking customers:

res/values/styles.xml # styles for all versions

res/values-v21/styles.xml # styles for API level 21+ only

res/values-es/styles.xml # styles for Spanish locale

You can read more about styles and themes at https://developer.android.com/guide/topics/ui/look-and-feel/themes

Themes

Style is applicable at the view level, but what if you want to apply the style to the entire app. Assume you want to apply a specific background color to the entire app. How would you do it? How would you specify which color to be used as a primary text and secondary text? You have to have used a theme for this if you want to do it in the best way. Check out the below code from our app and see how we are doing it:

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">

<!-- Customize your theme here. -->

<item name="android:windowBackground">@color/lightYellow</item>

<item name="colorPrimary">@color/colorPrimary</item>

<item name="colorPrimaryDark">@color/colorPrimaryDark</item>

<item name="colorAccent">@color/colorAccent</item>

<item name="android:textColorPrimary">@color/colorPrimary</item>

<item name="android:textColorSecondary">@color/grey</item>

<item name="android:textColorTertiary">@color/grey</item>

</style>

So ultimately theme is nothing but an extended version of specially designed style attributes that work on the application level.

Creating activities and fragments

Once we created all the required layout we will now prepare our main part of the application. This part will allow the user to see what we have created. This also allows the user to interact with our app. This is known as an Activity and a Fragment. Activity is a crucial part of the Android application. Without Activity, the user cannot see anything from your app. It's the Activity that holds the user interface. The primary objective of the activity is to set and present a user interface to your end-user. We have already created one simple Activity named SplashActivity. As a next step, we will create MainActivity. This new activity will hold now all sorts of data. We are not creating any more activity and from this point onwards we will rely more on Fragments. To help you visualize the flow, refer to below flow chart. It will give you a good idea about how the navigation will be working:

Figure 5.5

So now we know what all activities and fragments need to be created. Out of all these components, we have already created SplashActivity. Before we create MainActivity and other fragments, we need to decide a few more things, which are as follows:

What application architecture to use?

How navigation will work?

How to write testable code?

How we will make network calls to fetch/save the data?

All of these topics can be complex. It depends on your application size. And there is no single solution for all applications. It requires a very scalable approach.

Now Google aims to provide the best possible support to its development community. So far it has come up with quintessential tools, platforms, and open-source projects. For Android as well Google launched a concept called architecture components to ease the development of Android application. Architecture component has lots of things to offer under the hood of a single name. We will be using some of the architecture component concepts in our recipe application.

Architecture component

The architecture component is a set of libraries. These libraries offer well-defined patterns and concepts which help to design robust, scalable and testable code. The fundamental goal of the architecture component is to provide a solution to:

Managing lifecycle throughout your application, not just your Activity or Fragment class.

Managing the data in a way that it notifies the view the moment data is updated.

Maintaining the state of the view even when the application is rotated.

An easy way to deal with local storage and use it asynchronously.

As a solution to the above four android architecture component offers four API which is as follows:

Life cycle aware components: Makes any class aware of life cycle changes

LiveData: Notify the views when data is created or updated

ViewModels: Persist the state of view irrespective of orientation changes.

Room: An API compatible with RxJava, Flowable and LiveData to manage SQLite database.

You as an application developer has complete freedom over which one you want to choose for your applications. You can choose all of them or maybe just one. All library comes up with separate Gradle dependency and you only add those dependencies which you need. We are going to use ViewModel and LiveData mainly for our application development. For this please add below two dependencies in your app level build.gradle file, to add support for ViewModel and LiveData:

implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"

implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"

The value for lifecycle_version would be the one which is latest available from Google. For our application, I am using lifecycle_version=1.1.1. But we are developing the application in Kotlin so for Kotlin's support, there is an extension library available to achieve the same thing but the Kotlin way. For that, you will add -ktx as the suffix for each architecture component library you use. So above to dependency will become:

implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"

implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"

The setup is ready now. Let's move further with the architecture of our app. So far we have decided that what we are going to use to manage the state of the view and also updating it when the data is created/updated. But these are just libraries, we still need to decide how classes will communicate with each other. How we will structure the code. We do not want our activities or fragments to become god classes and have all types of code within those classes. It's not a scalable approach. It's also difficult to maintain and test on the longer run.

Below is the application's architecture diagram which demonstrates how the architecture component will play a key role in our application development and how to code will be structured:

Figure 5.6

The diagram is self-explaining each and every layer's responsibility. Still, let's devote bit more time and try to understand these layers:

A view controller is either your Activity or Fragment. The sole responsibility of this layer is to display the data and allow the user to interact with the app so that the user can input the data.

ViewModel will be holding your data. This data will be shown to UI with the help of LiveData. LiveData will notify the view once the data is updated.

After this comes the Repository layer. This will decouple the network calls from your application's logic. The repository layer will provide the data to your ViewModel.

So now architecture wise we are very much ready. We have created good enough flow diagrams, and architecture diagrams to visualize the code structure. Now it's the time to jump into the development. As a part of learning for this chapter, we aim to have a fully functional user interface. But we are going to learn a very interesting topic in the next chapter which is coroutines. Co routines will help us to fetch the data asynchronously. Since we are going to cover it in the next chapter we will perform each and every operation using some mock/dummy data. For data we are relying on https://www.themealdb.com they are the ultimate source of data for us. They freely provide a very good set of data not just for meal recipes. They also provide data for music albums and songs worldwide, cocktail information, etc. Our application's data structure is going to be highly influenced by the data they provide. The data layer classes will reflect these a lot.

Navigation components

The first task as a development we have is to set up a MainActivity. If you remember the flow diagram, MainActivity is showing three fragments. These fragments show various types of data. Each fragment has dedicated responsibility. As per our architecture diagram, each fragment will have a view model and it's the repository. The repository is currently returning mocked data for now, because we haven't performed network calls yet:

Figure 5.7

We have already learned about creating layouts. You can try creating these layouts using DSL if you want. I have created them using the traditional XML layout approach. As we will be using navigation components in this application, I will cover a bit of it in this section.

The navigation component is designed with the purpose of making the developer's life easy when it comes to managing the navigation of the app. Navigation is an important part of every application. It has a lot of todo with the behavior of your application. If not managed properly, your user will get lots of surprises with the screen they see or the data which is shown on screen. Imagine yours is a social media app and the user is going through the posts they uploaded on their timeline. Each post has a detailed screen linked with it when user taps on any particular post they go to detail screen. On this detail screen user presses the back button and suddenly user is taken to the home screen. Isn't it something which is unpredictable to your user? Your user will expect to go back to timeline screen.

Navigation components exactly solve this problem. You can manage the back stack and data passing very easily and that too in a centralized manner. Navigation component has below four primary goals:

Managing fragment transactions with back stack management.

Manage the back or up button behavior

Passing the data around the activities or fragments.

Controlling animations

Managing deep links

To get started with the navigation component, you need to add the dependencies in your project which is part of the Android jet pack. Add below lines in your app level build.gradle file:

implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"

implementation "androidx.navigation:navigation-ui-ktx:$nav_version"

The value of nav_version is 2.1.0 for the project which I have created. After adding these two dependencies we are ready to go.

Navigation component has the following things to learn:

Destinations: Each fragment is known as a destination.

Navigation graph: A visual tool to help to keep all navigation-related information together in the form of graph and it's data. A graph will show a list of destinations in a graphical manner.

Navigation host: An empty container that is solely responsible to host the destinations within.

Navigation controller: A class that keeps data about your navigation. This class knows what data to pass to which destinations.

Take a look at the snapshot of the navigation graph we have in our application:

Figure 5.8

So let's see how to set up something similar. What we want as a result is, MainActivity should display three fragments using the navigation graph.

Preparing navigation graph

To prepare a navigation graph you need to create entirely a new resource type. To achieve this follow the below steps. (Refer Figure 5.9 for reference):

Right-click on res directory and choose the Android resource file.

Enter file name as main_navigation or any valid name which you want.

Choose Resource type as Navigation. (This is the most important thing).

Figure 5.9

After this, a new resource directly would have been created and you will see main_navigation.xml file inside. After you open it by clicking it. You will have an empty navigation graph editor in front of you. The navigation editor is divided into three main parts:

The leftmost part is called Destinations, which displays all your destinations and navigation hosts.

The rightmost part which is called Attributes, which shows attributes for selected destinations or actions.

And the center part is known as Editor. Here is the visual representation of your complete navigation journey.

Let's add a navigation host to our Activity first. Go to layout file of your MainActivity.kt and add below code to add NavHost and BottomNavigationView. BottomNavigationView is required to display tabs in bottom which will let user switch between different fragments:

<?xml version="1.0" encoding="utf-8"?>

<androidx.constraintlayout.widget.ConstraintLayout

xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http://schemas.android.com/apk/res-auto"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

tools:context=".view.MainActivity">

<fragment

android:id="@+id/navHostFragment"

android:name="androidx.navigation.fragment.NavHostFragment"

android:layout_width="0dp"

android:layout_height="0dp"

app:defaultNavHost="true"

app:layout_constraintEnd_toEndOf="parent"

app:layout_constraintStart_toStartOf="parent"

app:layout_constraintTop_toTopOf="parent"

app:navGraph="@navigation/main_navigation"

app:layout_constraintBottom_toTopOf="@+id/bottomNavigationView"/>

<com.google.android.material.bottomnavigation.BottomNavigationView

android:id="@+id/bottomNavigationView"

android:layout_width="0dp"

android:layout_height="wrap_content"

app:menu="@menu/main_navigation_menu"

android:background="@color/lightYellow"

app:layout_constraintBottom_toBottomOf="parent"

app:layout_constraintEnd_toEndOf="parent"

app:layout_constraintHorizontal_bias="1.0"

app:layout_constraintStart_toStartOf="parent"

/>

</androidx.constraintlayout.widget.ConstraintLayout>

The BottomNavigationView is using main_navigation_menu.xml which is inside res/menu directory which looks like as follows:

<?xml version="1.0" encoding="utf-8"?>

<menu xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http://schemas.android.com/apk/res-auto">

<item android:icon="@drawable/whats_new"

android:id="@+id/whatsNewFragment"

android:title="@string/what_s_new"/>

<item android:icon="@drawable/by_category"

android:id="@+id/recipeByCategoryFragment"

android:title="@string/by_category"/>

<item android:icon="@drawable/by_regions"

android:id="@+id/recipeByRegionFragment"

android:title="@string/by_region"/>

</menu>

The menu will serve as an option to display tab items. Here if you observe the code which is marked bold, it is nothing but a fragment used from the Android API itself. Android navigation component provides a default Nav host named NavHostFragment. Also, we have specified which navigation graph the Nav host should use. We have used the app:navGraph attribute to indicate that.

After this, we will now add three fragments as destinations. We will use exclusively designed navigation editor for this. Go to your main_navigation.xml file and switch to design mode:

Click the New Destination icon on top of your editor to add a new fragment or activity as a new destination.

If you have fragments already created it will be automatically shown in the list as shown in Figure 5.11. Otherwise, you can select Create a new destination and it will open up a wizard to create a new Fragment:

Figure 5.10

As you can see in the snapshot we have name fragments as RecipeByCategoryFragment, RecipeByRegionFragment and WhatsNewFragment. Add all these fragments as part of your destinations. That's it, your BottomNavigationView with switchable fragments is almost ready.

Displaying fragments on MainActivity

Once the navigation graph is ready, it's time to use it. We are going to write just one line in Kotlin and boom!! We will have MainActivity with switchable fragments with BottomNavigationView ready. We will be using a heavy extension function to set up BottomNavigationView and Fragments which looks as follows:

bottomNavigationView.setupWithNavController(findNavController(R.id.navHostFragment))

So how this extension function works? Let's spend some time to unfold the mystery. The extension function will look like the following:

fun BottomNavigationView.setupWithNavController(navController: NavController) {

NavigationUI.setupWithNavController(this, navController)

}

It's nothing but a wrapper function over NavigationUI. NavigationUI is the part of the original API that comes as a part of Android SDK. This takes the instance of BottomNavigationView and NavController. So navigation-ui-ktx gradle dependency provided us this extension function to manage the navigation controller.

Another extension function which is used is findNavController. This function is as follows:

fun Activity.findNavController(@IdRes viewId: Int): NavController = Navigation.findNavController(this, viewId)

One more super useful function from navigation-ui-ktx. This internally calls a function from standard Navigation API. This makes it so easy that even a monkey can code this bottom navigation sheet.

Let's run the code and make sure everything works. The expected output would be your fragments should load and the app should not crash. All tab switching should work.

Creating data repository

At this point the basic navigation is set, let's try to work on the data now. We are going to focus on WhatsNewFragment as a part of this section. As per our architecture diagram, our data will be managed from a repository class. A repository patter is already a proven methodology to keep your data layer separate from your business logic. The principal responsibility of the repository layer will be to connect remote networks, perform HTTP request and bring data over TLS. Since we are not performing network calls as a part of this chapter, our repository will load data from assets directory in Android.

The repository is ideally connected to the service class named SearchRecipeService. This service class can load data from anywhere, we will follow the contract and implementation can be done by any class. Below is the contract for SearchRecipeRepositoryImpl class:

interface SearchRecipeRepository {

fun getLatestRecipes(): List<Recipe>

fun getRecipeOfTheDay(): Recipe

fun getAllCategories(): List<Category>

fun getAllRegions(): List<Region>

fun getRecipeByCategory(categoryName: String): List<RecipeBasicInfo>

fun getRecipeByRegions(regionName: String): List<RecipeBasicInfo>

fun searchRecipeByName(recipeName: String): List<Recipe>

fun getRecipe(recipeId: Int)

}

The SearchRecipeRepositoryImpl function will take instance of SearchRecipeService. The SearchRecipeRepositoryImpl class header can look like below:

class SearchRecipeRepositoryImpl(private val searchRecipeService: SearchRecipeService) : SearchRecipeRepository {

// Your implementation

}

We have added mock response inside the asset directory in form of .json file:

Figure 5.11

It is important to know how SearchRecipeService will look. This class is holding the implementation which loads data from assets:

class SearchRecipeService(private val assetManager: AssetManager) {

fun getLatestRecipes() = assetManager.open("latest_recipe.json")

fun getRecipeOfTheDay() = assetManager.open("recipe_of_the_day.json")

fun getAllCategories() = assetManager.open("all_categories.json")

fun getAllRegions() = assetManager.open("all_regions.json")

fun getRecipeByCategory() = assetManager.open("recipe_by_category.json")

fun getRecipeByRegions() = assetManager.open("recipe_by_regions.json")

}

The SearchRecipeService class is a temporary implementation, in the next chapter we are going to perform a refactoring to this class where the asset manager will be removed and network calls will be put in place. How these .json files will look like? It's important for us to know that. Only and only after we know this we can write the JSON parser or create a model class which will be mapped to .json files. You can take a look at https://www.themealdb.com/api/json/v1/1/random.php to get some rough idea about the response structure.

Reading input stream

All functions within SearchRecipeService class return InputStream type. Since we are storing files inside the asset directory and assetManager.open(<FILE_NAME>) function returns the instance of InputStream, we will end up dealing with an InputStream. It's again a time to put our learning in practice. We learned how extension function can be a great savior. And there is an already written extension function for reading an input stream, which goes like below:

searchRecipeService.getLatestRecipes().bufferedReader().use { it.readText() }

But this is still repetitive and boring if we have done this in multiple places. If we always wanted to read InputStream using BufferedReader and want a single String back, we can write one more extension function and make this entire call very simple. Below code would be that extension function which reads InputStream using BufferedReader in one go and return the instance of String as a result:

fun InputStream.readUsingBufferedReader() = this.bufferedReader().use { it.readText() }

And change searchRecipeService.getLatestRecipes().bufferedReader().use{it.readText()} to searchRecipeService.getLatestRecipes().readUsingBufferedReader(). This is going to be one punch knockout to InputStream. I would say One knockout is better than hundred punches.

Converting raw data into application data

The other cool trick you will learn here is how we convert raw data into the application-level data. Application-level data is much more meaningful and ready to process in the application. The repository class should map raw data to a much more meaningful format and objects. The benefit of doing this is again you decouple the low-level repository module from high-level ViewModel or Presenter type of classes. If the contract for your request or response is changed, your outer layer remains unchanged. By doing so you will save a lot of effort when it comes to refactoring the code or changing the API structure later on. The trick we did is, we have used extension function here. And extension functions are also easily unit testable. So we achieve loose coupling and testability together.

Let's take an example. The free API which we are using is returning below response for which we are using to display as Recipe of the day on our WhatsNewFragment:

{

"meals": [{

"idMeal": "52942", "strMeal": "Roast fennel and aubergine paella", "strDrinkAlternate": null,

"strCategory": "Vegan", "strArea": "Spanish",

"strInstructions": "1 Put the fennel, aubergine, pepper and courgette in a roasting tray. Add a glug of olive oil, season with salt and pepper and toss around to coat the veggies in the oil. Roast in the oven for 20 minutes, turning a couple of times until the veg are pretty much cooked through and turning golden.\r\n\r\n2 Meanwhile, heat a paella pan or large frying pan over a low\u2013 medium heat and add a glug of olive oil.",

"strMealThumb": "https:\/\/www.themealdb.com\/images\/media\/meals\/1520081754.jpg",

"strTags": "Vegan,Paella",

"strYoutube": "https:\/\/www.youtube.com\/watch?v=H5SmjR-fxUs",

"strIngredient1": "Baby Aubergine",

"strIngredient2": "Fennel",

"strIngredient3": "Red Pepper",

"strIngredient4": "Courgettes",

"strIngredient5": "Onion",

"strMeasure1": "6 small",

"strMeasure2": "4 small",

"strMeasure3": "1 thinly sliced",

"strMeasure4": "1 medium",

"strMeasure5": "1 finely chopped ",

"strSource": "https:\/\/www.homestylemag.co.uk\/recipe\/517\/main-courses\/roast-fennel-and-aubergine-paella",

"dateModified": null

}]

}

It's still not complete response and I have kept it short here for the example purpose. You see it's a strange response when it comes to attributes like strIngredient and strMeasure. And you as a developer would expect it to be of List type so that you can easily use them all over the places. So this type of conversion you can do it in the repository. And from that point onwards you have a list of ingredients and measures in the outer layer of the app. The extension function which I have written for that is as follows:

fun Meal.mapToRecipe(): Recipe {

return Recipe(

id = idMeal, recipeName = strMeal,

category = strCategory, region = strArea, imageUrl = strMealThumb,

ingredients = getIngredients(this), cookingInstruction = strInstructions

)

}

I like the extension function a lot, and I use them a lot in my application. One thing to keep it in mind is extension function is static functions so make sure you use it wisely.

Creating ViewModel with the help of generics

Since we are using ViewModel to develop our application obviously because of its benefits over Presenter. The ViewModel is very much controlled by the OS and you can't just instantiate anywhere. You must follow the process which is defined by android API. You typically instantiate any ViewModel like below:

val model = ViewModelProviders.of(this)[MyViewModel::class.java]

The important reason for this strict way of instantiating has very logical reasons. Your ViewModel is aware of lifecycle events. If they are not like that how would you expect them to maintain the state of your data and not to destroy them when your Activity is created? Did you notice in ViewModelProviders.of(this) you are passing a context? The context is of your life cycle owner which is either your Activity or Fragment.

So to instantiate any class a ViewModel we provide the class type of reference as an input and we expect the instance to be created and returned to us. But what if we want to pass some parameters to the view model type class. Which is demonstrated in the above example will not work for us. What do we do in this case? Well, Android has provided us another way to instantiate the view model. We can use the concept of the factory design pattern. We need to create a ViewModelFactory and implement ViewModelProvider.Factory. Take a look at the below code:

class ViewModelFactory(private val repository: SearchRecipeRepository) : ViewModelProvider.Factory {

override fun <T : ViewModel?> create(modelClass: Class<T>): T {

return WhatsNewViewModel(repository) as T

}

}

The ViewModelProvider.Factory interface will ask you to provide implementation to create function. Your implementation class can have constructors. You can pass parameters to this constructor and use it to pass the required values to your ViewModel classes. And to use factory class you use below type of code:

ViewModelProviders.of(this, viewModelFactory).get(WhatsNewViewModel::class.java)

Here you are seeing some usage of generics. Let's spare some time to learn more about generics in Kotlin.

What are generics?

Well as the name suggests, Generics are providing more capability and flexibility such that it can be adapted to any type. You program in such a general way keeping more than one type in mind. The biggest advantage of generics is even though you are supporting multiple types you provide the type of safety. We have already cover good enough in our Chapter 3: Go to the Depth of Kotlin.

Refactor view model factory using generics

In this section, we will be going to apply our knowledge and understanding of generics and provide the capability to our view model factory class to create any type of view models. We now know how we are overriding create a function for ViewModelFactory class. Let's reuse the same function and add support to all our view models. We have view model classes created to manage categories, regions, and search queries for our app. Below is the modified code which helps to initialize all the view model we have:

class ViewModelFactory(private val repository: SearchRecipeRepository) : ViewModelProvider.Factory {

override fun <T : ViewModel?> create(modelClass: Class<T>): T {

val viewModel = when {

modelClass.isAssignableFrom(WhatsNewViewModel::class.java) -> WhatsNewViewModel(repository)

modelClass.isAssignableFrom(CategoryViewModel::class.java) -> CategoryViewModel(repository)

modelClass.isAssignableFrom(RegionViewModel::class.java) -> RegionViewModel(repository)

else -> SearchRecipeViewModel(repository)

}

return viewModel as T

}

}

Now in WhatsNewFragment class, you use a code snippet demonstrated below to use inside factory class:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

super.onViewCreated(view, savedInstanceState)

val viewModelFactory = ViewModelFactory(SearchRecipeRepositoryImpl(SearchRecipeService(this.resources.assets)))

val viewModel=ViewModelProviders.of(this, viewModelFactory).get(WhatsNewViewModel::class.java)

}

So at this point, our WhatsNewFragment has it's view model and repository connected to each other. As a next step, we will not do all the needful steps.

Make sure you have prepared and added other required layout. The WhatsNewFragment contains the RecyclerView mainly. And the layout which represents the list item is named recipe_list_item_big.xml. We have written a function to display the recipe items in recycler view. But before we use it we need to fetch out the data from our view models. And both the functions together looks something like below:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

super.onViewCreated(view, savedInstanceState)

val viewModelFactory = ViewModelFactory(SearchRecipeRepositoryImpl(SearchRecipeService(this.resources.assets)))

val viewModel=ViewModelProviders.of(this, viewModelFactory).get(WhatsNewViewModel::class.java)

showRecipeOfTheDay(whatsNewViewModel.getRecipeOfTheDay())

showLatestRecipes(whatsNewViewModel.getLatestRecipe())

}

private fun showLatestRecipes(latestRecipe: List<Recipe>) {

whatsNewRecyclerView.run {

setHasFixedSize(true)

layoutManager = LinearLayoutManager(activity)

adapter = LatestRecipeAdapter(latestRecipe)

addItemDecoration(DividerItemDecoration(resources.getDimension(R.dimen.sixteen).toInt()))

}

}

In the function showLatestRecipes did you notice how we have used run function. We learned this during Chapter 2: Kotlin Fundamentals under section Scoping functions. We could have used it with function as well. The compiler will not have any problem even if you use functions like let or apply. But they are not just right for this use case. We do not want anything as a result or return value from this block, so we should not use it. Stress your mind and turn some pages back to Chapter 2: Kotlin Fundamentals again. Refer to the diagram which explains all the use cases around those functions.

Tip: Whenever you find that same object is being referred or used multiple times within any function or constructor, give a thought about possible usage of scoping functions. They are great and they will give tremendous readability to your code. Think Kotlin!

It's time to do a dry run of what we have done so far. Let us run the app and check the output. Testing what we have done on each possible point is very important. Test as much and as frequently as you can. The expected output should be as shown in Figure 5.7.

Troubleshoot your dry run

If you did not get the expected output, below are the points which you might want to check and make sure you have implemented corrected?

Have you added your activity in AndroidManifest.xml file?

Have you got the correct layout in place?

Have you got the correct view binding in place?

Do you have your view models and repository compiled?

Do you have your implementation in SearchRecipeRepositoryImpl.kt?

You can always seek help from the sample code which is on the GitHub.

Extension function and generics

So far we understood how we used generics and extension functions. Now there is a possibility you can use both together and create a function that will help to create view models using factory class for any fragment. Yes! You read it right. The way we create view models in each fragment is repetitive and can be extracted as a function. Check out the code which is as follows:

fun <T : ViewModel?> Fragment.createViewModelUsingFactory(modelClass: Class<T>): T {

val viewModelFactory = ViewModelFactory(SearchRecipeRepositoryImpl(SearchRecipeService(this.resources.assets)))

return ViewModelProviders.of(this, viewModelFactory).get(modelClass)

}

The function createViewModelUsingFactory is now an extension function that will work for any class/subclass which is of type Fragment. This function takes any class type of argument and that is defined as T. T can be any type that extends ViewModel. <T: ViewModel?> suggests the same. We should specify the return type as T as well because what we want to return is an instance of a subclass of ViewModel. Now we will have to use the following code to use the above extension function:

val whatsNewViewModel = this.createViewModelUsingFactory(WhatsNewViewModel::class.java)

Above is the simplest and efficient implementation of our problem.

Get rid of findViewById()

We put the good focus on generics and extension function, but did you even notice that there is no use of findViewById() function? Is it something cool? Binding each and every view using findViewById is the most boring step in Android application development. But with Kotlin you just don't need to worry about it. You will get this feature absolutely for free. You just have to add a Gradle plugin for this:

apply plugin: 'kotlin-android-extensions'

The extension is already added when you create an Android application with Kotlin's support. So you get the benefit of this feature without doing anything. Wondering how this works? Let me explain to you. We give id to any view element in the .xml layout file. When you refer to the IDs of these views in Activity or Fragment, you will require to add an import statement for this. Imagine there is an id whatsNewRecyclerView given to RecyclerView which is inside fragment_whats_new.xml layout file. So the import statement will be as following:

import kotlinx.android.synthetic.main.fragment_whats_new.*

You won't be able to see the implementation because that is hidden inside the plugin's implementation. But this will eliminate so many errors. The probability of referring to the wrong element and finding it not initialized is very less now. The documentation here https://kotlinlang.org/docs/tutorials/android-plugin.html#view-caching says that using findViewById() can be costly operations when view hierarchy is huge. So clearly for such scenarios using these kotlin-android-extensions can be very useful and will improve the performance of your application.

Data classes

We have put into practice most of our learning from Chapter 2: Kotlin Fundamentals. Data classes are just one of those learnings. Data classes are created where we only want to store the data in the form of instances. Such classes usually do not contain and business logic. In our application we have created two types of data classes:

Domain level data classes

View level data classes

Data level classes

Data level classes are involved to map raw data. Whatever we receive from the API as a JSON response will be mapped to these data classes using ORM (Object-relational Mapping) library called JSON. The structure of these data classes is always influenced by the structure of the JSON file received from the API or web services. We can create such data classes and put them inside a single file. All data classes can be kept in one file and treated as a top-level declaration. Our version of such domain-level data classes can be seen below:

import com.google.gson.annotations.SerializedName

data class Meal(

@SerializedName("idMeal") val idMeal: String,

@SerializedName("strMeal") val strMeal: String,

@SerializedName("strCategory") val strCategory: String?,

@SerializedName("strArea") val strArea: String?,

@SerializedName("strInstructions") val strInstructions: String?,

@SerializedName("strMealThumb") val strMealThumb: String,

@SerializedName("strYoutube") val strYoutube: String?,

@SerializedName("strIngredient1") val strIngredient1: String?,

@SerializedName("strIngredient2") val strIngredient2: String?,

@SerializedName("strIngredient3") val strIngredient3: String?,

@SerializedName("strIngredient4") val strIngredient4: String?,

@SerializedName("strIngredient5") val strIngredient5: String?,

@SerializedName("strMeasure1") val strMeasure1: String?,

@SerializedName("strMeasure2") val strMeasure2: String?,

@SerializedName("strMeasure3") val strMeasure3: String?,

@SerializedName("strMeasure4") val strMeasure4: String?,

@SerializedName("strMeasure5") val strMeasure5: String?,

val strSource: String?

)

data class Category(

val idCategory: String, val strCategory: String,

val strCategoryThumb: String, val strCategoryDescription: String

)

data class RecipeBasicInfo(val strMeal: String, val strMealThumb: String, val idMeal: String)

data class Region(@SerializedName("strArea") val strArea: String)

data class RecipeListResponse(val meals: List<Meal>)

data class CategoriesResponse(val categories: List<Category>)

data class RegionsResponse(val meals: List<Region>)

data class RecipeByResponse(val meals: List<RecipeBasicInfo>)

The best practice here would be to mark all the properties as val. Since these properties would be mostly used as read-only values, marking them as val would be a great practice. There is a risk involved if we mark it as var. The var can be changed at any time and such change would be prone to error.

Presentation level classes

The data level classes are kept in a separate package. The inner layers should not be dependent on the outer layer. And changes in an inner layer should not affect the outer layer. With this principle in mind, we created one more version of Recipe class which we will use to keep track of view related data. With such provision even if the data classes in the domain layer are changed, it will not affect data classes in the presentation layer. Hence you will have no changes in your view or presentation layer.

Dependency injection

We always create/initialize endless objects consciously and unconsciously throughout our application development. Without initializing instances we cannot use them at all. But have you ever thought how much time we put in this task? Initializing objects is not the purpose of your application development. You would love to focus on the actual problems and business logic of your application. I would say it's a negligible effort. Wouldn't it be great if the framework does it for you? Nowadays managing such type of dependency using a framework is considered a best practice in the industry. The concept is known as dependency injection (DI).

While I am writing this chapter we have the following dependency graph:

Figure 5.12

The above diagram shows how the application is mainly initializing instances of the above type. All the view models are depending on the SearchRecipeRepository instance. So if you go by the traditional approach and initialize the instances whenever needed, you will end up writing similar code on multiple places. DI frameworks help you with the approach where you can centralize object initialization logic and manage them easily. DI framework offers numerous benefits are as follows:

The code remains loosely coupled.

It can be tested easily and helps very much too mock dependencies.

It is ultimately your reusable code.

This follows the separation of concern principle.

The code becomes readable.

Originally this must have been evolved as some design pattern. Somebody might be wanted to keep all the object initialization in one place. The concept is fantastic. I was learning this for one of my projects in 2016 and I was looking for a simple explanation. I came across to an explanation which was not difficult to understand even for my not so techie wife. Here is that explanation:

"When you go and get things out of the refrigerator for yourself, you can cause problems. You might leave the door open, you might get something Mommy or Daddy doesn't want you to have. You might even be looking for something we don't even have or which has expired.

What you should be doing is stating a need, "I need something to drink with lunch." and then we will make sure you have something when you sit down to eat."

Comparison between various DI frameworks

The definition is easy to understand but not the frameworks. There are many popular dependency injection frameworks available for Android. They are as follows to list a few:

Dagger

Kodein

Koin

Guice

Each of these frameworks has its pros and cons. We will analyze them now:

Dagger2

Kodein

Koin

Guice

Uses reflection

No

No

No

Yes

Code generation

Yes

No

No

No

Supported platforms

JVM and Android

JVM, Android, JavaScript, and Kotlin native

Java, Android

Java, Android

Uses annotations

Yes

No

No

No

Developed in Kotlin

No

Yes

Yes

No

Community support

Great

Emerging

Good

Good

Documentation

Great

Good

Great

Good

Github stars

14.4K+*

2K+*

4.5K+*

8.7K*

* The data is based on their GitHub repository dated 30th Oct 2019.

Kodein and Koin are still new frameworks compare to Dagger2 and Guice. Dagger2 is the greatest and solid player in the club. But these new frameworks are so easy to implement that trend is moving towards them. The syntax is so clear. Kodein offers a very reach set of API. They can be a little complex for some developers. On the other hand, Koin has fewer features but it's very simple to set up and use. It has recently gained a lot of attention and increasing popularity on GitHub. I have tried Koin for our application. And it took me only 30 minutes to set up and make it work. It worked like a charm on the first run itself.

Using Koin

As per the documentation, Koin is A pragmatic lightweight dependency injection framework for Kotlin developers. It's entirely written in Kotlin. It's an open-source project and can be found at https://github.com/InsertKoinIO/koin. We have used limited features of Koin in our sample application. Let me inject some Koin in you :)

Setting up Koin

Just like any other dependency, Koin is also available for Gradle. It's available under jcenter repository. So make sure you have added it in your project level build.gradle:

repositories {

jcenter()

}

Now add the actual dependency in your app level build.gradle file as follows:

dependencies {

// Core dependency

implementation "org.koin:koin-android:$koin_version"

//Add only and only if you are using View models

implementation "org.koin:koin-androidx-viewmodel:$koin_version"

}

Sync your Gradle dependencies after making the above two changes.

Declaring modules

Now we will define modules. Modules are nothing but high-level categories that represent your dependency. If you have an internet connectivity capability in your app, you can represent it as a network module. If you have the Login feature in your app, there can be a Login module. The module is a way of indicating your DI framework about how to use these dependencies. Modules are your implementation to show how objects will be initialized. You can define as many modules as you want. Take a look at the below code snippet which demonstrate how the module can be defined:

val repositoryModule = module {

single { SearchRecipeService(androidContext().assets) }

single<SearchRecipeRepository> { SearchRecipeRepositoryImpl(get()) }

}

val viewModelModule = module {

//View model module

viewModel { WhatsNewViewModel(get()) }

viewModel { CategoryViewModel(get()) }

viewModel { RegionViewModel(get()) }

viewModel { SearchRecipeViewModel(get()) }

}

The modules are created using DSL in Kotlin. A couple of things here to talk about:

Passing Android context: Wherever you want to use context within the scope of Koin you refer to androidContext() function. This will make the context available to you.

Using get(): The get() function will bind required dependency from the dependency graph. Wherever you mention get() it look into the graph and automatically binds matching dependency to your initialization.

Apart from androidContext() and get() there are few other functions which you should know:

single: Using this function you can indicate Koin to declare that instance as a Singleton. Be very careful while using this, otherwise it can cause a side effect.

factory: Factory function will be used for everything else which is not a singleton. For example, Presenter shared preferences or any other classes in your app.

viewModel: An out of the box support provided for android view models. It's internally an extension function written over Module class. A must use the function if you have ViewModel functions in your application.

Starting Koin

Now since we have set up the modules it's time to initialize and configure those modules inside Koin. In its official documents, the terminology used is Starting the Koin. To start the Koin, we need to have a subclass of an Application class. We have created a simple RecipeApplication class in our project which is as follows:

class RecipeApplication : Application() {

override fun onCreate() {

super.onCreate()

startKoin {

androidLogger()

androidContext(this@RecipeApplication)

modules(listOf(repositoryModule, viewModelModule))

}

}

}

By the way, after you create any such class, don't forget to add it inside your AndroidManifest.xml file. You should add it using name attribute to application tag like shown in below code:

<application android:name=".RecipeApplication"> </application>

You just need to call startKoin{} function and put your module or list of modules inside that function. You also need to keep in mind that if you are using context to initialize any of your dependencies, you have to put androidContext() inside the startKoin function. And pass the application's context as a parameter of androidContext() function.

Injecting your dependencies

As the last step now we start injecting all the required dependencies before we start using them. You can inject your component using a simple function, that is, by inject(). If you want to inject view model you need to use by viewModel(). Take a look at the below code snippet.

private val whatsNewViewModel: WhatsNewViewModel by viewModel()

private val categoryViewModel: CategoryViewModel by viewModel()

We have mostly used view models so far, so currently we are not using them by inject() anywhere. Let me tell you that use of by is nothing but we are using delegated properties. We have delegated the initialization to functions like inject() or viewModel().

In Chapter 7: Kotlin-ize Remaining of your App we will do more refactoring and add more cases around using DI with the help of Koin.

Wrapping up

We have reached yet another end of learning and it's a time to introspect. Think of a three interesting thing that you feel you have as learning from this chapter.

First, I had a lot of fun while exploring architecture components. I liked the way view models are helping us restore the state of the view. Second, I enjoyed using a few of the Kotlin features like Extension function, generics, scoping functions, view binding and get rid of the findViewById completely. I now never have to write that boring code in any of my application, neither have I to rely on any third party view binding library. Third, I never felt using Koin would be this easy. I am pouring my heart out, I had so much fun. I further went to visit Koin's open source project at https://github.com/InsertKoinIO/koin and unfold a lot of mysteries. You should take a look as well.

Next chapter we will focus on connecting applications to the remote server. The most exciting part of the next chapter would be, use of co-routine. This upcoming chapter adds life to our application by connecting to the server and bringing up the real-data.

CHAPTER 6

Making Network Calls Using Coroutines

"It is far easier to design a class to be thread-safe than to retrofit it for thread safety later."

- Brian Goetz

Brian Goetz, in his book Java concurrency in practice, has said above quotes and it stands true for any programming languages which support multi-thread programming language. You might be thinking, why we are talking about thread safety and multi-threading programming when it is told that this chapter is for connecting our app with a remote server and bringing the data. Yes! Our aim is still the same. But in Android connecting your app to any remote server insist to do it asynchronously. Android has a constraint that long-running operations cannot be performed on the main thread. They can be only performed on the worker thread. It has to be asynchronous. Any long-running operations in Android cause Activity Not Responding error (ANR). When you perform anything on the main thread or UI thread for more than 5 seconds Android system will throw this error and your user will see and error as follows:

Figure 6.1

Figure 6.1 is the typical error dialog you might have seen during your application development. This means you have consumed a lot more time than expected. You are blocking your user to interact with the app or stopping them to provide any input. This is not considered good user experience.

Kotlin offers a nice feature known as coroutine to perform anything asynchronously.

Structure

The chapter covers the following topics:

What is coroutine?

Using coroutine

Understanding coroutine scope

Understanding the hottest feature of coroutine i.e. Flow

Connecting application to the remote server using a coroutine

What is coroutine?

Coroutine A coroutine is Kotlin's way of tackling asynchronous operations, multi-threaded programming or managing concurrency. But Java already has a well-defined and proven mechanism known as Threads, why do we need a new feature. What sort of problems it solves? Well, there is always a chance for improvement and so is Thread. According to Coroutine team, Java threads have below problems:

Context switching for the thread can be a costly operation.

Various The various OS does restrict the number of threads created by per application. They can be infinite.

There are still a few platforms that do not support threads. For example, JavaScript

It's always difficult to debug threads.

Dealing with race conditions can be a nightmare.

Coroutine has answered all the above problems. And they have various approaches to answer these.

Conceptually to understand you can compare it with Java's thread. Like a thread, coroutine also runs in parallel, they can communicate with each other and one can wait until another coroutine has not finished. And all these you get with no performance trade-off compare to threads.

Using coroutine

Let's start by setting up a simple project to experiment for coroutine. For this, we will go back to the IntelliJ IDEA. We need to create a simple Gradle project with Kotlin (Java) as shown in Figure 6.2:

Figure 6.2

The next step would be to enter groupId and artefactId. Which can be entered as shown in Figure 6.3:

Figure 6.3

And at last just provide any valid project name, for example, KotlinCoroutine. Please refer to Figure 6.4 for that:

Figure 6.4

Now to start with we should add below Gradle dependency in our project's build.gradle file:

dependencies {

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2"

}

After updating the dependencies sync your project or import changes and it will download the coroutine-core package. This Gradle implementation contains all core classes which are required for any coroutine related code.

Creating coroutine

Now we are ready to create our first coroutine. Creating a simple coroutine is an easy peasy. It's just a function call. Check out the below code snippet:

GlobalScope.launch {

//Your code goes here …

}

Anything you write or provide within the launch function will be run as a separate thread. Oh, I mean separate coroutine! :) We should develop a habit of not to refer this as thread. We just learned how coroutine and thread is an entirely different concept. Let us expand the example a bit more. Check out the below code and its output to develop the basic understanding about coroutine:

import kotlinx.coroutines.GlobalScope

import kotlinx.coroutines.delay

import kotlinx.coroutines.launch

fun main() {

println("Received an order to prepare burger and diet coke.")

GlobalScope.launch {

delay(3000)

println("Preparing burger")

}

GlobalScope.launch {

delay(2000)

println("Preparing coke")

}

println("Next order please!")

Thread.currentThread().join()

}

You will notice how these two launch functions are running separately. This is the simplest form of a coroutine example. Launching any type of coroutine always require a scope. There are various types of scope you can use based on your application's needs. In the above example, we are using GlobalScope everywhere. It's not something great we have done and you should avoid doing that. If you dig down more into the scope, you will find out, why it's not a wise step.

Understanding the scope

As you know that without scope it's not possible to launch or start a coroutine. GlobalScope is so handy to use that one can just start using it. It may not have a visible impact on your program's performance, but it's never recommended for your applications. Because as the name says GlobalScope is not specific to any particular thread, it's owned by your application or program. It gets launched and runs like a top-level coroutine. This happens under the application's lifetime and they never get canceled prematurely. This can keep running in the background. If you think, such kind of situation where you want something in background continuously running is not ideal. Why would you have such a requirement? So one should always avoid using this. Instead of GlobalScope, I would recommend using a CoroutineScope. The below code demonstrates, how you can use CoroutineScope. You will also learn, how you can fetch the name of currently running coroutine name:

fun main() {

println("Thread : ${Thread.currentThread().name} Received an order to prepare burger and diet coke.")

CoroutineScope(Default).launch {

delay(2000)

println("Thread : ${Thread.currentThread().name} Preparing coke")

}

CoroutineScope(Default).launch {

delay(3000)

println("Thread : ${Thread.currentThread().name} Preparing burger")

}

println("Thread : ${Thread.currentThread().name} Next order please!")

Thread.currentThread().join()

}

// Output

Thread : main Received an order to prepare burger and diet coke.

Thread : main Next order please!

Thread : DefaultDispatcher-worker-1 Preparing coke

Thread : DefaultDispatcher-worker-4 Preparing coke

The CoroutineScope is an interface. It has nothing but a property of type CoroutineContext. This means any type of CoroutineScope will have some sort of context. It could be an empty context or default context or UI context, etc. Each CoroutineScope has a Job attached to it. And it's the job which is essential and performs tasks asynchronously. The instance of Job will give you more control over coroutine and you can manage the Job the way you want. But how do you get the instance of Job?

Creating a Job

A Job in coroutine is something which is cancellable and has a lifecycle. Job has a parent-child relationship as well. Where if the parent job is canceled all child jobs associated with the given parent job will be automatically canceled. A Job in coroutine can have three states, i.e. active, canceled and completed. In Figure 6.5 the lifecycle of a coroutine Job can be seen:

Figure 6.5

The lifecycle is fairly simple to follow and understand. The detailed explanation can be found at https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html

Keep below table Table 6.1 handy while you work with Job:

State

is Active

is Completed

is Cancelled

New (optional initial state)

false

false

false

Active (default initial state)

true

false

false

Completing (transient state)

true

false

false

Cancelling (transient state)

false

false

true

Cancelled (final state)

false

true

true

Completed (final state)

false

true

false

Table 6.1

Whenever you create a coroutine using launch or async or any other way, those function always returns the instance of type Job. This instance is useful and it gives full control over coroutine. You can easily manage the coroutine, for example, keeping it in wait mode or canceling it. Following code block will demonstrate how to get the instance of a Job:

runBlocking {

val orderJob = CoroutineScope(IO).launch {

println("Placing your order for ${listOf("Boiled vegetable Salad", "Mushroom curry", "Punjabi flat bread")}")

delay(3000)

val orderId = "ORDER_${Random(999999).nextInt()}"

println("Your order has been placed, order id is $orderId")

}

delay(5000)

}

The instance val orderJob is the key instance now. This will let you manage your coroutine fully. For example, after we create a Job we have put an explicit delay just for the sake of allowing enough time to complete the job. Rather than relying on a delay function wouldn't it be nice to tell the program that waits until the job is finished! So how do you do that? If such a thing is possible that would be ideal and that's called coroutine management. Since it's very much possible and it's the very obvious expectation from a coroutine. Let's remove the explicit delay and add a code that will wait until the job is finished. We will use join() function for this. Let's take a look at the following code block:

runBlocking {

val orderJob = CoroutineScope(IO).launch {

println("Placing your order for ${listOf("Boiled vegetable Salad", "Mushroom curry", "Punjabi flat bread")}")

delay(3000)

val orderId = "ORDER_${Random(999999).nextInt()}"

println("Your order has been placed, order id is $orderId")

}

orderJob.join()

}

Now, this is what we needed. If you relate this with the real-time situation, you don't need to worry about what time the coroutine will take to complete. The program will simply wait until it's finished. The job is done!

Canceling a Job

What if you want to cancel the job while it's running? There can be such circumstances in the application lifecycle where you may want to cancel the job to save the resources. For example, you were making an API call and the user navigated to another screen or minimizes the app. In this case, you would like to cancel the running job. There is no point in fetching data in the background if the user is not going to see it. To cancel any job, you need to retrieve the instance of that respective job and call a cancel function on it. Copy and paste the below code snippet in a new file and try to observe the output:

fun main() {

runBlocking {

val orderJob = CoroutineScope(IO).launch {

println(

"Placing your order for ${listOf(

"Boiled vegetable Salad",

"Mushroom curry",

"Punjabi flat bread"

)}"

)

delay(5000)

val orderId = "ORDER_${Random(999999).nextInt()}"

println("Your order has been placed, order id is $orderId")

}

delay(3000)

println("Customer is tired of waiting and now cancelling the order")

orderJob.cancel()

}

}

In the example above the orderJob is canceled. And after we call .cancel() function the orderJob will be no longer processed and you will not see it's the final output. The .cancel() function can be invoked from anywhere within its instance's scope.

Managing asynchronous coroutine

Launching a coroutine is an easy peasy job. But what if you want to get a result back from the job. The join() function do not help you to track the coroutine and it does not return the result. To get the result from coroutine you need to start the coroutine in a little bit different way. The async function will help us to get the result back. Take a good look at the following code:

fun main() = runBlocking {

val deferredResult = async {

delay(3000L)

"Veggie treat"

}

println("Your coke is ready, waiting for burger…")

println("Here is your burger, ${deferredResult.await()}")

}

If you run the above code, instantly you will notice the difference between launch and await. Truly defining the meaning of its name, await function awaits until the job is finished and gives you results back. But wait, where we are returning the result. The last statement within the async block becomes your return statement. You can return any type of data from the async function. The signature of an async function is as follows:

public fun <T> CoroutineScope.async(

context: CoroutineContext = EmptyCoroutineContext,

start: CoroutineStart = CoroutineStart.DEFAULT,

block: suspend CoroutineScope.() -> T

): Deferred<T>

The CoroutineScope function's extension function async returns the instance of Deferred<T> where T can be any generic type. In the context of reactive programming, you can imagine Deferred as an Observable. So in our example "Veggie treat" is Deferred<String>. And .await function is a subscriber over your observable. So .await() awaits for the Deferred<T> and once the job is processed it captures the result and uses it further.

But hey, with the instance of Job I was able to cancel the job as well. What will happen now if I want to cancel the job and I do not have an instance of it? Because async doesn't return the Job, it returns the completely new type Deferred. Don't worry! It's not true. Deferred is a subtype of Job. Deferred is defined as an interface which extends the Job interface:

public interface Deferred<out T> : Job{

}

Here please bear in mind that both Deferred and Job are an interface hence they need to be implemented by some class and the class which will implement it has to provide implement to functions like cancel(), join(), or await().

To understand the difference between async/await and launch/join, try to run below functions one after another, and you will find out the difference by yourself:

/**

* Awaits for the result. Once the job is finished captures the result and process it.

*/

fun main() = runBlocking {

val deferredResult = async {

delay(3000L)

"Veggie treat"

}

println("Your coke is ready, waiting for burger…")

println("Here is your burger, ${deferredResult.await()}")

}

/**

* Fire and forget type of a call. If the thread is alive the job will be keep

*/

fun main() = runBlocking {

launch {

delay(3000L)

println("Here is your burger veggie treat.")

}

println("Here is your coke.")

}

/**

* Just adds a simple delay and nothing else

*/

fun main() = runBlocking {

delay(3000L)

println("Here is your coke and burger veggie treat.")

}

Tip: In the above example try to understand the tone of a println() statement to get the gist. The tip is, use launch if you do not worry about the result of a coroutine. Fire it and forget it. But use async if you do care about the result and want to use it further in the program.

Suspending a function

So far, we have got a good enough understanding of coroutines to starts with. We tried out some small examples. But we all know that in real projects and application these type of examples just doesn't work. They are good for understanding purposes only. We perform much more complex tasks in the actual application. We write code in the form of units i.e. reusable functions. Any implementation we write inside coroutines can be possibly a lot of lines of code. In this case, you would extract out as a function to give more readability and testability. Take a look at the below code and even if it's doing a simple task you will love to extract it as a common function:

fun main() {

runBlocking {

val orderJob = CoroutineScope(IO).launch {

println(

"Placing your order for ${listOf(

"Boiled vegetable Salad",

"Mushroom curry",

"Punjabi flat bread"

)}"

)

delay(5000)

val orderId = "ORDER_${Random(999999).nextInt()}"

println("Your order has been placed, order id is $orderId")

}

delay(3000)

println("Customer is tired of waiting and now cancelling the order")

orderJob.cancel()

}

}

With the aim of extracting the piece of login you create functions something like below:

fun main() {

runBlocking {

val orderJob = CoroutineScope(IO).launch {

val orderId = prepareOrder(listOf("Boiled vegetable Salad", "Mushroom curry", "Punjabi flat bread"))

println("Your order has been placed, order id is $orderId")

}

orderJob.join()

}

}

private fun prepareOrder(orderList: List<String>): String {

println("Placing your order for $orderList")

delay(3000)

return "ORDER_${Random(999999).nextInt()}"

}

But you will soon notice that it doesn't work like this. You will see a compiler error as shown in Figure 6.6:

Figure 6.6

So, what is this suspend function? A suspend function can be executed inside a coroutine. If you refer to the philosophy of stackless coroutine, you would be able to understand that why coroutine needs a suspend-able function. Coroutine or functions within the coroutine are never managed through the stack. A keyword suspend indicates that it's capable of running as a coroutine. Similarly, if a function is calling any coroutine as its implementation, it has to be marked with suspend keyword. Suspend function has the capability of being paused and resumed at any given time. And this pause/resume philosophy is the main trait of the coroutine. Let's resolve the above compilation error first. To fix the compilation error you just add a suspend keyword. And the new function should look like as follows:

private suspend fun prepareOrder(orderList: List<String>): String {

println("Placing your order for $orderList")

delay(3000)

return "ORDER_${Random(999999).nextInt()}"

}

So, what magic suspend keyword does? Let's unfold the mystery. We will decompile this function. And after decompiling the code you will see a code as follows:

final Object prepareOrder(@NotNull List orderList, @NotNull Continuation $completion)

The decompiled code strangely adds the new parameter of type Continuation and removes the suspend keyword in it's Java version. Continuation is an interface written by Kotlin team which is part of its standard API. And this interface has a property and a function. Below code extract from the source code will help you understand it better:

@SinceKotlin("1.3")

public interface Continuation<in T> {

/**

* The context of the coroutine that corresponds to this continuation.

*/

public val context: CoroutineContext

/**

* Resumes the execution of the corresponding coroutine passing a successful or failed [result] as the

* return value of the last suspension point.

*/

public fun resumeWith(result: Result<T>)

}

By looking at the code we can clearly say that each suspend function gets the instance of their context. Context is holding it's environment-related information. And then there is a resumeWith function. And it takes a Result type as an argument that holds the value of the last successful or failure point. And the whole thing roams around the game of callbacks. This is how coroutine functions internally. And this is the reason any suspend function cannot run without coroutine. It has to be either run inside coroutine or called from other suspend function. You can create a hierarchy of suspend function but on top-level it will be some coroutine. And coroutine always has a context, this is how Kotlin's team will make sure that coroutine doesn't go wrong.

Measuring coroutine's performance

We talked about the coroutine and it works a lot. But we haven't seen whether it improves the performance or not. And until we see the numbers we don't believe. It's human nature. I have created a simple example where we can measure that coroutine improves performance.

The approach here would be creating two functions. One coroutine will perform the synchronous job and the other one will perform jobs asynchronously. The input given to the coroutine is the best-case scenario in terms of complexity. We are going to sum numbers ranging from 1 to 1000. The range of these numbers will be divided into four equal sizes i.e. 250 for each lot. That is, 1000/4=250 these are equally distributed lot sizes.

Here to measure the time taken by the coroutine job we are going to use one predefined higher-order function. Kotlin's standard API has a function that does this. The function is measureTimeMillis and its code is as follows:

public inline fun measureTimeMillis(block: () -> Unit): Long {

val start = System.currentTimeMillis()

block()

return System.currentTimeMillis() - start

}

It does a simple thing, collects the starting time and executes the given function. Once the function is performed it punches the time again and subtracts the start time. A simple formula.

Let's create a suspend-able function that will sum the given range of numbers. And to simulate the long-running operation we have just added a delay of 100 milliseconds. The sum function is as follows. And it returns the Int which is the sum of the given range:

suspend fun sum(startNum: Int, endNum: Int): Int {

delay(100)

return (startNum..endNum).sum()

}

Now the sum function will be tested with and without asynchronously running coroutine. We have created two functions for that.

sumWithoutAsync

This function will sum 1..1000 as a single synchronous task. We make the calls to the sum function four times in synchronous fashion, collect the result and at the end, add the results to get the final results. The function will print the totalTime before it terminates. The function is written as follows:

fun sumWithoutAsync() = runBlocking {

val totalTime = measureTimeMillis {

val sum1 = sum(1, 250)

val sum2 = sum(251, 500)

val sum3 = sum(501, 750)

val sum4 = sum(751, 1000)

println("Sum is ${sum1 + sum2 + sum3 + sum4}")

}

println("Total time without coroutine: $totalTime")

}

sumWithAsync

This sumWithAsync function will sum 1..1000 with four asynchronous tasks. The for async calls will carry out the summation and the result will be recorded. And the final summation will be calculated before function terminates. And of-course we also record the total time taken by this function. The function is written as follows:

fun sumWithAsync() = runBlocking {

val totalTime = measureTimeMillis {

val sum1 = async { sum(1, 250) }

val sum2 = async { sum(251, 500) }

val sum3 = async { sum(501, 750) }

val sum4 = async { sum(751, 1000) }

println("Sum is ${sum1.await() + sum2.await() + sum3.await() + sum4.await()}")

}

println("Total time with coroutine: $totalTime")

}

We call the above two functions inside our main and run them multiple times. Following is the code and result of the main function. The program was run 5 times. The output of a function which was running asynchronously is highlighted and visible difference in time taken to produce the output can be noticed. I have shortened the output and removed unwanted noise of other println() statements:

fun main() {

sumWithAsync()

sumWithoutAsync()

}

//Output 1

Total time with coroutine: 168

Total time without coroutine: 409

//Output 2

Total time with coroutine: 147

Total time without coroutine: 412

//Output 3

Total time with coroutine: 104

Total time without coroutine: 411

//Output 4

Total time with coroutine: 106

Total time without coroutine: 415

//Output 5

Total time with coroutine: 102

Total time without coroutine: 416

Beyond coroutine Job – A Flow

We now understand the greatness of coroutine. We have verified its performance as well. A coroutine can help us to perform anything off the main thread. And this is as easy as snapping your finger. Also, you don't need to write boilerplate code. But this is not a reason we use coroutine. We like them because after launching them managing a job is easier compare to any other thing. By canceling the job we cancel a whole lot of things. This easy to deal philosophy makes us love coroutine. A coroutine is more than Job and Context and Scope. Whatever we have learned so far will help us working asynchronously. What if I want to work reactively? There is a difference in this. In Android and many widely used frameworks and platforms, reactive programming is the culture. It's highly suitable for the mobile ecosystem. Because each action on mobile has a reaction. You perform a particular task to get some results back. You tap the button and expect to be navigated. You swipe up to see more content on your device screen. This demand performing tasks not just asynchronously but also reactively. React is nothing but an asynchronous task followed by the data stream. And that's exactly the concept of Flow in a coroutine. The official definition of Flow is…

A cold asynchronous data stream that sequentially emits values and completes normally or with an exception.

The Flow is also said to be cold. This means they only run if they are being triggered. And collect is an official terminology for this. The emitted value can only be used if it's collected. In the context of RxJava this means the published value can only live if subscription takes place. If you do not subscribe/collect the published/emit will not stream any type of data. Flow is highly inspired by reactive streams and frameworks e.g. RxJava and project reactor.

Emit and collect a Flow

We are gonna take a very simple example. As a part of this example, we will simply keep emitting a stream of data for an uncertain amount of time and from the main thread, we will collect this data. Imagine a river that has a continuous flow of water. How would you represent it in the form of code? Take a look at the below code and you will know:

private fun flowingRiver(): Flow<Water> = flow {

while (true) {

delay(100)

emit(Water())

}

}

The function flowingRiver continuously emits the instance of a class Water. Its nature is a never-ending flow of the type of water. But, the water needs to be collected if you want to use it. So how do we collect water? By what means we collect the flow? Check it out the below code snippet:

fun main() = runBlocking {

flowingRiver().collect { value -> println("Drop of $value") }

}

You just need to call the function collect and this is how to keep getting the data. This is how you use the data and process them further. It's a dead-simple concept.

Inside Flow

So what's happening under the hood? How this Flow works. Flow is an interface with one abstract function collect:

public interface Flow<out T> {

@InternalCoroutinesApi

public suspend fun collect(collector: FlowCollector<T>)

}

Flow is designed with only one aim i.e. collect. The signature for function flowingRiver indicates that the return type is Flow<Water> and the implementation calls another function named flow. Do get confused between Flow<T> and flow. Latter is a function that safely executes the block of code. It also internally marks it with suspend. Take a good look at the function signature and its implementation:

public fun <T> flow(@BuilderInference block: suspend FlowCollector<T>.() -> Unit): Flow<T> = SafeFlow(block)

This means anything you pass as an argument to flow function is nothing but your suspend-able function.

If you go one more level down there you will encounter a class SafeFlow. The SafeFlow class has an implementation of a collect function. The collect() function further creates the instance of SafeCollector. This SafeCollector has an implementation which is making everything possible. There you will find an implementation of emit function.

Canceling a Flow

Once you collect the data, you may wish to cancel the collection. Here, in this case, you do not have an explicit cancel() or any similar function. The instance of a Job that is collecting the flow has to be canceled. This will automatically cancel the Flow. Let's take a look at the following example:

fun main() = runBlocking {

val job = launch {

flowingRiver().collect { value -> println("Drop of $value") }

}

delay(3000)

job.cancel()

}

Operators in Flow

The data stream which is emitted can change its original form. There could be various reasons where you want to modify the original data and transform it into a different type or value. Assume your data source is emitting all types of numbers but you want to only collect even numbers. So you have to transform the flow. There are a variety of operators available. You can choose one or many operators as per your needs. The key takeaway here would be in Flow you can call the suspend-able function inside these operators. The operators in Flow has very familiar terms like map, filter, transform, zip, etc. We will understand all the major operators here.

map operator

For the operator map, you can apply any kind of transformation. You can remove the data from the source, modify the data or you can modify the whole type. To give you an example, please take a look at the below-demonstrated code and see how the type Water is modified into another type AcidicWater:

fun main() = runBlocking {

val job = launch {

flowingRiver()

.map { pollutionByWeThePeople() }

.collect { value -> println("Drop of salty water $value") }

}

delay(3000)

job.cancel()

}

fun pollutionByWeThePeople() = AcidicWater(Random.nextInt(1, 10))

class AcidicWater(val pH: Int) {

private val hydrogen = 2

private val oxygen = 1

override fun toString(): String = "H$hydrogen O$oxygen pH=$pH"

}

Once the flow has been started using flowingRiver() function, we can map each incoming object. Here we are trying to transform Water(), entirely into a different type. From the function pollutionByWeThePeople() we map it to type AcidicWater. So now for any operator which is used further to map operator they all will receive a new type AcidicWater. In function collect you will get the instance of type AcidicWater and use it as per your need.

filter operator

This is conceptually the simplest extension function ever. This extension function is written for any Flow<T>. It is a higher-order function that takes a function as an argument. The function which is passed as an argument should return a Boolean value. If the return value is true the record is processed further otherwise it's discarded from the Flow. The below example will explain to you and give a better idea about how filter function works:

fun main() = runBlocking {

val job = launch {

flowingRiver()

.map { pollutionByWeThePeople() }

.filter { it.pH >= 7 }

.collect { value -> println("Drop of drinkable water $value") }

}

delay(3000)

job.cancel()

}

In the example, it's clearly understood that how Boolean result is processed and the collect function will be called for all those instances of AcidicWater where pH level is more than 7.

Note: In technical terms map and filters are known as operators, but they are just specially designed functions. And all the tasks and features function have in Kotlin can be seen in these functions as well. These two functions/operators will be the most commonly used functions for any of the examples where Flow is used. They are also known as terminal functions. collect is also a terminal function. But it is the most basic one. Also from these functions, you cannot emit further. It will give you a compile-time error.

The terminal function collect sits in the bottom place when it comes to the hierarchy of the terminal functions. Other functions can be used multiple times and do not necessarily be called in a particular order. You can call functions like map, filter, fold, reduce as many times as you want. And whatever value is returned can be captured in any of the objects you want and can be used further in your program. These all terminal functions have the capability to collect the flow.

Behaviour The behavior of Flow is sequential

Flow is running asynchronously, but the operators and terminal functions run top to bottom. They are sequential. So after the flow is created, they go off the main thread and runs through all functions and operators sequentially. Figure 6.7 depicts its behavior visually:

Figure 6.7

In Figure 6.7, the left side shows the emit and right side imitates the behavior of the collect function. Each box indicates the time taken by a function emitting the Flow or collect a Flow.

Assume each box has an execution time of 1 second. Now try to demystify the diagram. After the Flow is emitted, immediately it is being collected by the collect function. The same behavior will continue until the last element is emitted and collected

So as per Figure 6.7 if each block takes 1 second to complete the execution, this means the Flow will take a total of 6 seconds and a few more milliseconds. 3 seconds to emit and 3 seconds to collect. And extra milliseconds are your actual time which JVM has taken to process your coroutines. Let's try to see in the form of an example:

fun main() = runBlocking {

val timeTaken = measureTimeMillis {

getThreeIngredients().collect {

println(it)

delay(1000)

}

}

println("Total time $timeTaken")

}

fun getThreeIngredients(): Flow<String> = flow {

delay(1000)

emit("Salt")

delay(1000)

emit("Pepper")

delay(1000)

emit("Potato")

}

In the above code snippet, the getThreeIngredients() will emit three ingredients with an interval of 1 second and collect function will receive it and imagine it is also taking 1 second for it. Following is the output:

// Output

Salt

Pepper

Potato

Total time 6035

This proves that flow is sequential. If you add operators like map, transform, take, filter, etc, it will create a longer chain of communication. And they will be performed sequentially as well.

Optimize the Flow using the buffer

When dealing with the sequential behavior of the data stream, there is a chance of improvement. Kotlin's team saw this possibility and came up with a context of Buffer. With the buffer, you can speed up the emitting. Logically thinking, if your collect function takes a long time to execute, why emit function and other stream operators should suffer! The idea of the buffer is to keep collecting the data and put it in the buffer object. The collect function will pick data from this buffer object and buffer will keep relaying the data. This way you can surely reduce some time and optimize your coroutine.

Let's take an example where we will compare the performance impact of the buffer. We will see how the buffer improves it. Assume there is a function called downloadVideo() which emits the data periodically. Inside main() function the data is collected. We have added delay() function wherever it is required to simulate the long-running operation. The main() function has two types of implementation, one without buffer and another is with buffer. The pictorial representation of how a buffer works are shown in Figure 6.8:

Figure 6.8

In above Figure 6.8, the block in the center represents the state of the buffer. All emitted data will go in a buffer and buffer will relay it to collect function as and when they are completed. We can see how all emissions can finish early. The collect function will continue their behavior. There you cannot optimize much. Check out the following code which proved the significance of buffer:

fun main() = runBlocking {

val timeTaken = measureTimeMillis {

downloadVideo()

.collect {

println("Downloading video $it%")

delay(1000)

}

}

println("====== Total time to execute is $timeTaken ======")

val timeTakenWithBuffer = measureTimeMillis {

downloadVideo()

.buffer()

.collect {

println("Downloading video $it%")

delay(1000)

}

}

println("====== Total time to execute is $timeTakenWithBuffer ======")

}

fun downloadVideo(): Flow<Int> = flow {

emit(20)

delay(1000)

emit(40)

delay(1000)

emit(60)

delay(1000)

emit(80)

delay(1000)

emit(100)

delay(1000)

}

//Output

Downloading video 20%

Downloading video 40%

Downloading video 60%

Downloading video 80%

Downloading video 100%

====== Total time to execute is 10047 ======

Downloading video 20%

Downloading video 40%

Downloading video 60%

Downloading video 80%

Downloading video 100%

====== Total time to execute is 5064 ======

See how significant time difference is. The performance is increased by half of the time. Imagine a complex Flow where using buffer can create a remarkable difference.

Managing multiple flows

So far we learned,

How we can create Flow?

How to emit and collect data?

What are the common operators to transform the data?

How to optimize the Flow?

But we learned all of the above with a single Flow. What if we have multiple instances of Flow to manage? What if we want to merge the result of two Flow? What if for any given two Flow, one finishes before others? All these questions need to be answered. And it is very much likely to happen for your application.

For this Kotlin is offering a few more operators. Using these operators you can merge the result of emit. The data which is emitted from two separate flows can be combine and transform into another type and later on become available to be collected. The two major functions which help to generate composed Flow is the zip and combine. They both are significantly different when compared to each other. In this section, we will focus particularly on these two operators.

The zip operator

As the name suggests, it combines the value emitted by two individually running flow. It looks forward to two corresponding values. To help you conceptually understand the zip operator, I have again try to explain it with the diagram. The diagram is as follows:

Figure 6.9

The analogy will remain the same here. The boxes represent time take by either emit or collect function. The left side indicates emitting data and the rightmost line shows how collect function will work. While working asynchronously it's possible that two coroutines will not run and behave in the same manner. Let's of the thing depend on OS and how the processor is written. When we create zip function the function is aware of both flow and it is zip function's responsibility to merge emitted data. If Emit 1 takes 1 second to complete and Emit A takes 2 two seconds to complete, zip will manage that delay. The zip function won't collect another emit until both flows have finished their first emit. Both concurrently running flow. Let's take a look at the example to get a better understanding. Assume we have following two Flows:

fun getIngredients(): Flow<String> = flow {

emit("Aubergine")

emit("Onion")

emit("Tomatoes")

emit("Garlic")

emit("Green Chili")

emit("Red Chili Powder")

emit("Oil")

emit("Coriander Leaves")

emit("Salt")

}

fun getUnitValues(): Flow<String> = flow {

emit("1 large")

emit("1/2 cup")

emit("1 cup")

emit("6 cloves")

emit("1")

emit("1/4 teaspoon")

emit("1.5 tablespoon")

emit("1 tablespoon chopped")

emit("as required")

}

Both functions emit exactly 9 values. Using a zip operator we will combine these two flows and create a new result that is available to be collected. The main function which uses these flows will look like the following:

fun main() = runBlocking {

val job = launch {

getUnitValues()

.zip(getIngredients()) { unit, ingredient ->

"$unit $ingredient"

}

.collect { println(it) }

}

job.join()

}

//Output

1 large Aubergine

1/2 cup Onion

1 cup Tomatoes

6 cloves Garlic

1 Green Chili

1/4 teaspoon Red Chili Powder

1.5 tablespoon Oil

1 tablespoon chopped Coriander Leaves

as required Salt

When you run above function observe the output of collect function. The zipping functionality is highlighted in the above code snippet. It's going as a lambda function to zip operator. The data is transformed into a single String instance and collected further.

As you are seeing that getUnitValues and getIngredients are emitting exactly 9 values. But in a real application, it is not this case always. But the zip operator will be your real savior and will not consider if either of the flow emits data in odd numbers. They have to evenly match. Try emitting extra data from either of the function and you will get the real behavior of the zip operator.

The combine operator

In the zip operator, its primary responsibility is to consolidate the result of two flow. Regardless of the time taken by either of the flow, zip will just wait for both until the emit function finishes its work. This is also not always an ideal scenario. Sometime you would just like to keep emitting and collecting. A practical example of such a scenario would be a group of workers collecting packed and processed food items from the food processing factory's conveyor belt. See Figure 6.10:

Figure 6.10

In such cases there would be a continuous flow of food item and plenty of workers would be available to collect the data. Each worker picks up whatever item available to them on the belt. This is exactly the combine operator does. The operator combine picks up the most recent value available in the flow. Here combine operator plays the role of workers as shown in Figure 6.10 and each flow item will be tomatoes.

Assume there are two flow emitting the data. When the flow is collected using the combine operator, it will pick up the latest data emitted by both the flow and will combine them and produce a new result. See the following code in action:

fun main() = runBlocking {

= launch {

getUnitValues().onEach { delay(100) }

.combine(getIngredients().onEach { delay(1000) }) { unit, ingredient ->

"$unit $ingredient"

}

.collect { println(it) }

}

job.join()

}

We have also used on Each{…} function. The function is simple, on Each will perform the given task first before emitting the flow. The delay function is added here to generate a time gap between getUnitValues() and getIngredients() flow. You can trace the above example with the help of Figure 6.11:

Figure 6.11

In Figure 6.11 follow the boxes on both the side and observe how arrows are pointing to the final result. The boxes which represent the time taken by each emit function, are random. And the center boxes are always referring to the one which is currently running. It doesn't wait for any particular emit function to complete before collecting the data.

Note: A major thing to remember here is both zip and combine operator is still in the experimental stage. This means there could be some API difference in its stable version. But the concept will remain the same. For zip, remember it will always respect the time taken by either of the emit function and wait for them until they both finishes. But the combine respects the time of the collect function. It immediately collects the data the moment either of the flow emits it and picks up the most recent value from another flow.

Operator combine without extension function

The zip and combine operator which we just learned now is an extension function written for class Flow. And they only accept two arguments. The first parameter is a Flow as an argument and another one is lambda function which transforms the flow. What if you want to combine multiple flows. Do you recall zip operator from RxJava? Flow has the same operator but here they name it different. The operator which works for multiple Flow is known as a combine operator. Don't mistake it with the combine operator which we just learned. Let's take a look at the function signature:

// Extension function

public fun <T1, T2, R> Flow<T1>.combine(flow: Flow<T2>, transform: suspend (a: T1, b: T2) -> R): Flow<R>

// Regular top level function

public inline fun <T1, T2, T3, R> combine(

flow: Flow<T1>,

flow2: Flow<T2>,

flow3: Flow<T3>,

@BuilderInference crossinline transform: suspend (T1, T2, T3) -> R

): Flow<R>

As you see in the above code snippet, the second function can accept three flows which are denoted by T1, T2, T3. R represents the result which is generated by combining the result of these three instances of Flow. The combine function has four such variants. Ranging from two flows to five flows. Given that it is understood that a combine operator can combine a maximum of 5 flow and generates one result. Let's take a look at the below code snippet which demonstrates this behavior:

fun getNumber(): Flow<String> = flow {

emit("1"); delay(500)

emit("2"); delay(500)

emit("3"); delay(500)

}

fun getAlphabets(): Flow<String> = flow {

emit("A"); delay(700)

emit("B"); delay(700)

emit("C"); delay(700)

}

fun getAlphabetsLower(): Flow<String> = flow {

emit("a"); delay(1000)

emit("b"); delay(1000)

emit("c"); delay(1000)

}

fun main() = runBlocking {

val timeTaken = measureTimeMillis {

combine(getNumber(), getAlphabets(), getAlphabetsLower()) { one, A, a ->

"$one $A $a"

}.collect { result ->

println("Final result $result")

}

}

println("====== Total time to execute is $timeTaken ======")

}

//Output

Final result 1 A a

Final result 2 A a

Final result 2 B a

Final result 2 B b

Final result 3 B b

Final result 3 C b

Final result 3 C c

====== Total time to execute is 3075 ======

The normal combine function keeps it's USP the same as an extension function. It's USP was to always combine the most recent value of all given Flow. As a parameter for R (result), we have used lambda function and the result from each given Flow is shown as one, A and a.

Tip: Who says Kotlin does not support semicolons? Did you notice how semicolon (;) can be used to reduce the number of lines and group related statements? getNumber, getAlphabets, and getAlphabetsLower use it to group emit and delay function. Kotlin still respects semicolon. Only if you use it at the end of the statement, it will ignore and do the job for you. Kotlin by default considers \n (newline character) to indicate the end of the statement.

combine vs combineTransform

The only difference between combine and combineTransform is, combineTransform has the capability to emit the data further. As the name suggests it can transform the data. The difference is made clear in the following code snippet:

fun main() = runBlocking {

val timeTakenTransform = measureTimeMillis {

combineTransform(getNumber(), getAlphabets(), getAlphabetsLower()) { one, A, a ->

emit("$one $A $a")

}.collect { result ->

println("Final result $result")

}

}

println("====== Total time to execute with transform is $timeTakenTransform ======")

}

If you go one layer down, it's explained why combineTransform can emit the data. The parameter which is for result R takes any function of type FlowCollector<R>. And emit is the extension function of FlowCollector.

Exception handling in Flow

We learned quite a lot for coroutine and Flow. But have you ever wondered, that what if any of the Flow either emit or collect terminated prematurely because of some run time exception? An exception can occur while processing any kind of instruction. What are the possible ways to handle such type of exception in Flow? In RxJava there was an onError() function, which we could call and get to know easily when there is any error while working with the data stream. Kotlin uses a simple try/catch/finally mechanism to handle any type Exception. In this section, we will learn, how errors scenario can be managed using exception handling. Following is the simple example:

fun main() = runBlocking {

val damCapacity = 50 ; var waterLevel = 0

try {

flowingRiver().collect {

check(waterLevel <= damCapacity) { "Dam capacity reached" }

println("Water level ${waterLevel++}")

}

} catch (e: Exception) {

println("!!! ALERT !!! : ${e.message}")

} finally {

println("Water level logged and action taken")

}

}

In this example try block has the entire code which emits and collects the data. The catch/finally will be blocks followed by try block. finally is optional. In Kotlin the philosophy of try/catch and finally remains the same as Java. If any exception occurred during the execution of flow the execution will be terminated from that point and control will jump to catch block. Once the catch block is processed control will go to finally block if provided else executes the next line after the catch block. The check function is part of Preconditions.kt which checks the given condition and throws IllegalStateException if the condition is not met. You can also throw any type of Exception from anywhere in the flow. Take a look at the following code:

fun main() = runBlocking {

val damCapacity = 50; var waterLevel = 0

try {

flowingRiver().collect {

if (waterLevel >= damCapacity) {

throw IllegalStateException("Dam capacity reached")

}

println("Water level ${waterLevel++}")

}

} catch (e: Exception) {

println("!!! ALERT !!! : ${e.message}")

} finally {

println("Water level logged and action taken")

}

}

In both, the example above every type of exception will be caught, regardless of where it occurred. The problem here is, it is not possible to know where the flow is terminated. It would be nice to know whether the exception is thrown from emitter or any of the operator or a collect block. The idea here is to keep Exception transparent.

catch as an operator

To achieve the exception transparency Kotlin offers to catch as an operator. Just like operator or terminal functions, for example, map, filter you can use catch as well. Have look at the following code:

fun main() = runBlocking {

val damCapacity = 50; var waterLevel = 0

flowingRiver()

.onEach {

check(waterLevel <= damCapacity) { "Dam capacity reached" }

}

.catch { e -> println("!!! ALERT !!! : ${e.message}") }

.collect { println("Water level ${waterLevel++}") }

}

As you can see inside onEach function the exception takes place and an immediate catch operator catches it. Until the condition is not met, the collect block will keep running and the moment condition is met, Exception will be thrown and catch operator will take over the flow. The output of the above code would be as following:

Water level 0

Water level 1

Water level 2

.

.

.

Water level 50

!!! ALERT !!! : Dam capacity reached

Please be aware that the catch operator is still in the experimental stage.

Declarative catch

The catch operator catches only and only upstream Exception. This means any exception which occurs after the catch block will not be noticed by the catch operator above it. The catch function comes last in the series of all operators. The catch function does not return anything. It returns the type of Unit. The try/catch block for complete flow as seen in the first example of this section looks very ugly. The declarative nature of the catch can solve this problem. To get the benefit of this approach, you can lift the body of collect function out of it and put it inside onEach{…}. And at the end just call the collect() function without lambda function:

fun main() = runBlocking {

val damCapacity = 50; var waterLevel = 0

flowingRiver()

.onEach {

check(waterLevel <= damCapacity) { "Dam capacity reached" }

println("Water level ${waterLevel++}")

}

.onCompletion { cause ->

cause?.let { println("The dam became full") } ?: println("River is empty but the dam is not full")

}

.catch { e -> println("!!! ALERT !!! : ${e.message}") }

.collect()

}

And this makes exception handling so easy and readable. You do not need to use an explicit try/catch block.

Tip: Use onCompletion function known as declarative handling. This could be used as finally. The best deal with onCompletion operator is, you get to cause as a nullable lambda receiver argument. The null cause indicates the flow completed without any problem. And not null cause parameter will carry reason, i.e. Exception's message. the cause is an instance of Throwable type. Please keep in mind that onCompletion is still in the experimental stage.

Connect application to the remote server

So far we learned how to perform operations asynchronously. We also learned how to manage the data stream using Flow. This coroutine and Flow will enable us to deal with the network layer. Now is the time to connect our application to a remote server and manage some real data. We will be highly relying on a third-party HTTP client i.e. Retrofit. Retrofit is a type-safe HTTP client written for Java and Android. It's owned by Square, Inc. Read more about Retrofit at https://square.github.io/retrofit/

Setting up dependency

So far for this chapter, we were practicing coroutines and Flow as a separate Gradle project. Now we need to add the dependency in our Android application. Inside your app level build.gradle please add following dependencies:

//Network calls

def retrofit_version = "2.6.2"

implementation "com.squareup.retrofit2:retrofit:$retrofit_version"

implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"

//Coroutines

def coroutine_version = "1.3.2"

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version"

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine_version"

//Live data and view model

def lifecycle_version = "2.1.0"

implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"

implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-rc02"

implementation "android.arch.lifecycle:extensions:1.1.1"

Most of the dependencies are self-explaining. For dependency livedata-ktx we are using release candidate where there are some extension functions which we would like to use. After adding these dependencies let's sync those Gradle dependencies.

Once the dependencies are synchronized we will be doing some heavy refactoring in a few of the classes.

Refactoring repository layer

While designing an architecture, we created a repository layer so that we can perform network operations there. The repository layer is having an instance of SearchRecipeService class which loads data from assets. We do not need local and mocked data. So let's delete that file.

We will create an interface with the same name i.e. SearchRecipeService. In Retrofit, you specify your API requirement in the form of interface with the help of some annotations. And the library will generate an implementation for you. So the interface will look like the following:

interface SearchRecipeService {

@GET("random.php")

suspend fun getRecipeOfTheDay(): RecipeListResponse

@GET("categories.php")

suspend fun getAllCategories(): CategoriesResponse

@GET("filter.php")

suspend fun getRecipeByRegions(@Query("a") region: String): RecipeByResponse

@GET("search.php")

suspend fun getRecipeByName(@Query("s") recipeName: String): RecipeByResponse

}

These are the few of the functions which we have defined. You might have noticed that we specify each function with suspend keyword. The reason is simple, we want that function to be asynchronous and want to execute off the main thread. Retrofit 2.6.0 now supports coroutines.

After you make these changes, your SearchRecipeRepositoryImpl will start complaining. In this class as well we will be making some drastic changes. This SearchRecipeRepositoryImpl is implementation for SearchRecipeRepository interface. We will change this interface to support Flow. Following is the glimpse of how SearchRecipeRepository is refactored to provide support of Flow:

// Without coroutine

interface SearchRecipeRepository {

fun getLatestRecipes(): List<Recipe>

fun getRecipeOfTheDay(): Recipe

fun getAllCategories(): List<Category>

fun getAllRegions(): List<Region>

fun getRecipeByCategory(categoryName: String): List<RecipeBasicInfo>

fun getRecipeByRegions(regionName: String): List<RecipeBasicInfo>

fun searchRecipeByName(recipeName: String): List<Recipe>

}

// With coroutine and Flow support

interface SearchRecipeRepository {

suspend fun getLatestRecipes(): Flow<List<Recipe>>

suspend fun getRecipeOfTheDay(): Flow<Recipe>

suspend fun getAllCategories(): Flow<List<Category>>

suspend fun getAllRegions(): Flow<List<Region>>

suspend fun getRecipeByCategory(categoryName: String): Flow<List<RecipeBasicInfo>>

suspend fun getRecipeByRegions(regionName: String): Flow<List<RecipeBasicInfo>>

suspend fun searchRecipeByName(recipeName: String): Flow<List<Recipe>>

}

Once the interface is refactored, it's time to refactor the actual implementation. Let's take the example of getRecipeOfTheDay function. The current implementation is parsing the InputStream and map it to RecipeListResponse type. If the mapping is successful we extract the first element from it, and with the use of the Meal.mapToRecipe() extension function, we generate the instance of type Recipe. And this instance of Recipe is passed to ViewModel. The current implementation is as follows:

override fun getRecipeOfTheDay(): Recipe {

return gson.fromJson(

searchRecipeService.getRecipeOfTheDay().readUsingBufferedReader(),RecipeListResponse::class.java

).meals.firstOrNull()?.let {

it.mapToRecipe()

} ?: throw Exception("No recipe of the day found")

}

But as a part of the new implementation, which is going to use Flow and coroutine, will look like the following:

override suspend fun getRecipeOfTheDay(): Flow<Recipe> {

return flowOf(searchRecipeService.getRecipeOfTheDay())

.map { it.meals }

.map { it.first() }

.catch { e -> throw Exception("No recipe of the day found") }

.map { it.mapToRecipe() }

}

In the above code snippet, we have just done something revolutionary. We are writing a suspend-able function that transforms and emits the data stream. Let's take a pause and understand everything in the next section.

Retrofit, Coroutine, and Flow

In this chapter so far we used coroutine, Flow and LiveData. They are used on various layers throughout the application. For example, we used coroutine and Flow at the repository and ViewModel level. LiveData is used at ViewModel and View level classes. Below diagram Figure 6.12 will give more insight and help you to gain more idea:

Figure 6.12

Retrofit is just an API client, there is a lot of other procedures that you need to follow, e.g. Inside repository extract the data from the response. Process collected data in view models and emit it to the view layer. And view classes will simply display it on the surface of the device.

Inside Repository

The only responsibility of the repository class is to perform network calls. It connects to the webserver over HTTPs and brings the data. After the data is received the body is mapped to the given data class and JSON is mapped to a simple object. In this entire process, we have used coroutine my marking all functions in the repository with suspend keyword. And once the data is consumed, we converted it into a Flow using flowOf function. flowOf function is part of flow builder functions. There are few functions available in core coroutine API which helps you to build the flow very easily. flowOf function can be used to convert any type of data into Flow. For example:

flowOf(searchRecipeService.getRecipeByName(recipeName))

Internally flowOf function just uses flow function and emits whatever value you pass. Following is the code of flowOf function:

public fun <T> flowOf(value: T): Flow<T> = flow {

emit(value)

}

You can avoid using Flow in the repository layer. We have already marked function with suspend keyword, so whenever it will execute it will always be on another coroutine. The benefit of flow would be where you can use operators like map, filter, combine, zip, etc. This makes code so much readable and manageable and will make the developer's life easy.

Inside ViewModel

In our example, any instance of ViewModel has an instance of the repository. The result (A data which is emitted by Flow) of the repository will be collected inside the view model. The instance of ViewModel will also have the responsibility to polish the data. Since the repository emits the Flow, we can use all operators based on our needs, which is provided by Flow.

Flow is a part of a coroutine, so we can say that we are using coroutine for at the ViewModel layer. All application logic can be performed inside ViewModel and when we use Flow, we can say that even application logic is performed off the main thread. This makes our application very efficient because you do not consume the time of the main thread here.

After we collect the data, we further emit the data. But this time we are using LiveData to emit the data. Instead of LiveData we could also use Flow here. Nothing wrong in it. But why we have used LiveData here? The main reason for using LiveData is not because they behave like observers. They are not ordinary observers. They are lifecycle aware of observables. They can respond whenever Fragment or Activity is created, started, resumed, paused, or destroyed. This could help view model to perform some critical operations which are related to these lifecycle events. Following is the code from WhatsNewViewModel class where after collecting Flow, data is emitted using LiveData:

fun getLatestRecipe(): LiveData<List<Recipe>> {

return liveData {

repository.getLatestRecipes().collect {

emit(it)

}

}

}

Inside View

And inside WhatsNewFragment the LiveData can be observed, just like any other observer. Following code does the same thing:

whatsNewViewModel.getLatestRecipe()

.observe(this@WhatsNewFragment, Observer { latestRecipe ->

whatsNewRecyclerView.run {

setHasFixedSize(true)

layoutManager = LinearLayoutManager(activity)

adapter = LatestRecipeAdapter(latestRecipe)

addItemDecoration(DividerItemDecoration(resources.getDimension(R.dimen.sixteen).toInt()))

}

})

This is not an example of coroutine. The observers of LiveData is completely different. But as I said before, there is nothing wrong with using a Flow to collect data from ViewModel. This would make a good pattern as well. If you use Flow you get the benefit of coroutines by default.

Wrapping up

To summarize here, a coroutine can be your weapon to deal with asynchronous tasks. It provides a lot of easy APIs to deal with it. Conceptually they are a lot similar to Thread but the performance they behave very differently compared to Thread. If you use Flow you can greatly control your data and a lot of flow operators come for free as a part of standard coroutine library. This will remove the need for any other third-party library or any reactive programming framework. While you use Flow to make sure you check whether that API is part of the experimental API or not. It is not safe to use experimental API on production. It involves risk. Experimental API can be heavily modified or removed when they release to developers.

With this chapter, we have almost finished our application development. It's now connected to the webserver. In the next chapter now we will be giving the final touch to our application. There is lot of other Kotlin feature which we can use to make our code better.

Let's Kotlinize our remaining application!

CHAPTER 7

Kotlin-ize Remaining of Your App

“Computers are good at following instructions, but not at reading your mind."

— Donald Knuth

In a previous chapter, we connect our app to a remote server and displayed actual data on the screen. Once the data is displayed on the screen, there is still a lot to do to make our application robust, efficient and production-ready. We still have to handle errors. In any application which connects to the remote server has a lot to think when it comes to error. For example, what if the internet is not available? What if the server returned the response which is not expected?

There is a lot of possibilities to refactor and improve the code. We can use a few more useful Kotlin extensions and improve the code. We will be Kotlin-izing remaining of our app.

Structure

The chapter covers

Error handling approaches

Extension function is a new way to write a utility function

Improve code using ktx and Splitties extension APIs

Convert build.gradle from groovy to Kotlin DSL

At the end of this chapter, our application would be much more production-ready. We are adding a graceful error handling. We will be learning two possible approaches for error handling. Along with error handling, we will also refactor the code to achieve this. Simultaneously let us also beautify the code using some extension libraries, for example, Android KTX and Splitties. And most importantly we are going to learn how we can convert build.gradle into Kotlin language.

Error handling approaches

Error handling is very important from the user’s perspective. It will let your user know what is happening in the system when something is not correct. In any application, there can be many scenarios that can cause a malfunction in your application. For example, while reading the file, the file is not available. Or when trying to get data from the server, the internet is not available. May be user has turned off the GPS and your application is trying to fetch the location. Endless scenarios can be listed based on the size and nature of the application. A poor error handling will leave your user in frustration and they may not ever come back to use your app. You should try to understand the nature of each and every type of error and should respond with the appropriate message to your user.

There is a whole science behind how the error should be handled gracefully. There are a lot of user experience research articles available on the World Wide Web. It is not just how you handle the error, but it’s equally important how you display it. All of you must have used Windows OS during your early age of life, where you started learning computers. Those who have used Windows OS must be familiar with how terrible they are when it comes to some type of error message. See Figure 7.1 and ask yourself what you understand from it:

Figure 7.1

These types of error handling are as good as no error handling. It does not make any sense to your happened and what they should do to overcome it.

Anyways in this section, we are going to put more focus on how error can be handled, because as an application developer this is the thing we can do best.

Handling exception inside the repository

For performing network operations, we have designed a repository class. All network calls are going through this class. It’s a centralized system that can connect to the internet using retrofit client-side API. This layer will return a valid response in JSON format if everything is correct. But if any error comes up, we should catch it on this layer first. We should transform the caught error in more meaningful error and emit it further. Figure 7.2 will help you to visualize how the error is processed on each layer of our application architecture.

The error is processed in the top to bottom manner. The repository layer is the first class that encounters the error whenever anything goes wrong in HTTP calls. The repository class catches these errors using catch operator of Kotlin Flow. Here these errors will get a meaningful name. We have written transformError() function inside Repository class. The transformError() function interprets the HTTP Exceptions and throws a custom exception. These custom exceptions are further caught and processed by the view model class. Any view model class must extend ViewStateAwareViewModel which is an abstract class. This class has the implementation of error handling. There is another way of error handling which we have demonstrated for WhatsNewViewModel class. Both approaches are correct in their way. Before we discuss both the approaches please take a look at Figure 7.2 once again and understand how the error information is passed through each layer:

Figure 7.2

In SearchRecipeRepositoryImpl.kt class we have a function written named getRecipeOfTheDay(). Here we have done simple error handling which is as follows:

override suspend fun getRecipeOfTheDay(): Flow<Recipe> {

return flowOf(searchRecipeService.getRecipeOfTheDay())

.map { it.meals }

.map { it.first() }

.catch { e -> throw Exception("No recipe of the day found") }

.map { it.mapToRecipe() }

}

There is a plain and dumb catch operator which tries to transform any error into a simple Exception class with a meaningful message. In the above code snippet, there are two places that I have highlighted. We will be adding our error handling code to these two places.

Refactor emitting the response

We are using flowOf function as a flow builder to convert regular data into the stream of data. The instruction is given as flowOf (searchRecipeService.getRecipeOfTheDay()). A compiler typically processes the function calls from right to left, this means getRecipeOfTheDay() will be called first and after this, the response will be converted into Flow. Here is a problem, what of getRecipeOfTheDay throws the exception, which it will if internet is not available. These types of exceptions will not be caught by catch operator. Because catch operator will only catch those exceptions which are thrown from any of the Flow builder or any Flow operator. So this means we have to write the flow builder in such a way that it could throw the Exception which a catch operator can handle. For this, we will write a new top-level function using generics.

Let’s name this new function as flowOfRecipe. The philosophy behind this function would be it can take a function with a return type of generic value T, as an argument which. The function flowOfRecipe returns an instance of Flow. Take a look at the following code and we can have visualized the above words which I have stated:

fun <T> flowOfRecipe(f: suspend () -> T): Flow<T> = flow {

try {

emit(f.invoke())

} catch (e: Exception) {

when (e) {

is IOException -> throw NoInternetAvailableException()

else -> throw SomethingWentWrongException()

}

}

}

Each of the lines in the above function is very crucial. The function could have been marked as an internal as it cannot be used by any other classes outside the application. Ideally, it should be marked as package private. But unfortunately, Kotlin does not have such a thing, so we cannot mark it as package private. You can move this inside SearchRecipeRepositoryImpl as a private function. The function has a try-catch block written inside. The function f which we receive as a parameter will be invoked using invoke function and if it is successfully invoked the result will be emitted by emit function else the catch block will be executed. The emit function will just work like a charm here because whatever we are getting as a response is further converted into Flow data stream.

The catch block will catch-all type of exception and map it to some custom exception classes which we have defined. The repository layer will keep everything very simple here. It only cares about two types of exceptions. These two exceptions are nothing but NoInternetAvailableException and SomethingWentWrongException. Retrofit throws IOException type if it is not able to connect to the internet. The exception could be a direct instance of IOException or subtype of it, for example, UnknownHostException. In any case, we are listening to parent type, that is, IOException and transforming it into NoInternetAvailableException. Any exception other than this can be treated as SomethingWentWrongException. Here things can go nasty if you are trying to transform a variety of HTTP errors. For example, there is a range of errors from 400 to 500 and each of these errors has very specific meaning. It depends on your application how you want to handle all this error very minutely. This also depends on how your APIs are designed. If APIs are returning relevant information along with this HTTP code it becomes easier to design an error handling layer.

Anyways, now let’s try to use flowOfRecipe function. Following code demonstrate how our recipeOfTheDay function is using flowOfRecipe function and leveraging error handling with the help of its catch operator:

override suspend fun getRecipeOfTheDay(): Flow<Recipe> {

return flowOfRecipe { searchRecipeService.getRecipeOfTheDay() }

.onEach { delay(2000) } // Just to simulate time taking API call.

.map { it.meals }

.map { it.first() }

.catch { e -> transformError(e) }

.map { it.mapToRecipe() }

}

Since flowOfRecipe is throwing the error the highlighted catch operator in the above code snippet can catch all types of exception occurred.

You might have noticed that the catch operator is not immediately placed after the flowOfRecipe function. But it’s placed much below that function. Why is that so? The reason is very logical. We are leveraging the concept of Exception transparency and declarative catching together. These two concepts we learned in Chapter 6: Making network calls using Coroutine under the Exception handling section. The operator catch catches everything above it. By placing the catch operator right after the fetching the first item from response enable us to catch all possible errors which can occur while reading the data. This includes any HTTP exceptions or any data processing exceptions. With a similar understanding and idea of error handling, we should further modify all flowOf builder function into to flowOfRecipe builder function throughout the repository class. Following code snippets highlights the changes to be done in functions like getLatestRecipe() and searchRecipeById():

override suspend fun getLatestRecipes(): Flow<List<Recipe>> {

return combine(

flowOfRecipe { searchRecipeService.getLatestRecipes() },

flowOfRecipe { searchRecipeService.getLatestRecipes() },

flowOfRecipe { searchRecipeService.getLatestRecipes() },

flowOfRecipe { searchRecipeService.getLatestRecipes() },

flowOfRecipe { searchRecipeService.getLatestRecipes() }

) { mealOne, mealTwo, mealThree, mealFour, mealFive ->

val recipeOne = mealOne.meals.first().mapToRecipe()

val recipeTwo = mealTwo.meals.first().mapToRecipe()

val recipeThree = mealThree.meals.first().mapToRecipe()

val recipeFour = mealFour.meals.first().mapToRecipe()

val recipeFive = mealFive.meals.first().mapToRecipe()

listOf(recipeOne, recipeTwo, recipeThree, recipeFour, recipeFive)

}.catch { e -> transformError(e) }

}

override suspend fun searchRecipeById(recipeId: String): Flow<Recipe> {

return flowOfRecipe { searchRecipeService.getRecipeById(recipeId) }

.map { it.meals } .map { it.first() } .catch { e -> transformError(e) } .map { it.mapToRecipe() }

}

As you are seeing all mystery behind exception handling is further transferred into transformError() function of the repository class. We put the complexity inside this transformError() function because we want to reuse them. The transformError() function is as following:

private fun transformError(e: Throwable): Nothing {

throw when (e) {

is NoSuchElementException -> NoRecipeOfTheDayFoundException()

is NoInternetAvailableException -> e

else -> SomethingWentWrongException()

}

}

The only task of this function is to transform errors into three main types of exceptions, which are as follows:

NoSuchElementException if no recipe found inside meal array.

NoInternetAvailableException if the user has turned off the internet or there is any connectivity issue.

SomethingWentWrongException if error is not either NoSuchElement Exception or NoInternetAvailableException.

The transformed exception is further thrown and the function returns nothing. So recall Chapter 3: Go to the Depth of Kotlin where we learned about the type system in Kotlin. We use Nothing type to indicate that function returns nothing but guarantees to throw some sort of Exception if something is not appropriate. Thus keeping this in mind it is a good approach to specify Nothing as a return type for function transformError(). The transformError() will remain private to the repository as no other class is supposed to use this function.

Show error to the user

The error handling, we have done so far is only limited to the repository layer. And the repository layer has nothing to do with what the user should see and how the user should see it. That responsibility is given to the view of the model layer. It is clear from Figure 7.2 that the view model will further process the output of transformError(). The classes like WhatsNewViewModel, CategoryViewModel will perform additional application logic on the output of transformError() function. Here come the two different approaches we were talking about. Figure 7.3 depicts how the error will be visible on the view. To display error on the view we will be performing the following steps:

Observe the error inside the catch operator for each of the view model classes.

Emit the error type or instance bundled inside RecipeResult.Error as approach 1.

Or update the specific live data instance that is observed by Fragment as approach 2.

Both of the above approaches would make more sense to you once I explained it to you in detail in this section.

Note: Both approaches are correct in their way, it depends on your application’s design and scale. I prefer approach 2.

Figure 7.3

Approach 1: Using a sealed class

In one of the examples Google has explained, I found this approach. The example is about architecture components. There were a few particular scenarios where it was demonstrated how the error can be passed from one layer to another layer. The example can be found here https://github.com/android/architecture-components-samples/tree/master/GithubBrowserSample

The approach is fairly simple, you can produce three types of instances from once class i.e. success, failure, and progress. These three instances are nothing but three data members of the same class. Take a look at the code which is inside the Resource.kt file of the above project. The data class Resource holds three types of properties which are status, data, and message. And the logic around it is, if the status is success refer to the data property and if the status is failure refer to the message property. I slightly modify this approach and make it more Kotlin friendly. Let’s dig down deep into this approach.

Let’s create a class name RecipeResult. And inside this class, there will be three more nested classes named Success, Error, and Loading. The RecipeResult is going to be sealed class and with this, we want to declare that no other type can be part of this class. Only these three nested classes are treated as RecipeResult. All three nested classes will be holding some sort of data. Take a look of the following code snippet which shows how the RecipeResult class will work:

sealed class RecipeResult {

data class Success<T>(val result: T) : RecipeResult()

data class Error<T : Throwable>(val error: T) : RecipeResult()

data class Loading(val loading: Boolean) : RecipeResult()

}

From the above code here is the description of all elements:

Success class will hold any type of result since it is defined as generics T.

Error class can hold an instance of type T. T can be any subclass of Throwable.

Loading class can hold a boolean where true means show the loader and hide the loader if false.

RecipeResult class behave as a sealed class which wraps all these nested class and represent them under the same name and its possible subtype.

This approach is used only in one place, that is, WhatsNewViewModel and WhatsNewFragment. On the other places approach, 2 is used. The WhatsNewViewModel has a function getRecipeOfTheDay() which emits LiveData<Recipe>. With the error handling and use of RecipeResult the same function will not return LiveData<RecipeResult>. Take a look at the following code snippet which highlights two different versions of the same function i.e. with and without error handling:

// Without error handling

fun getRecipeOfTheDay(): LiveData<Recipe> {

return liveData {

repository.getRecipeOfTheDay().collect {

emit(it)

}

}

}

// With error handling

fun getRecipeOfTheDay(): LiveData<RecipeResult> {

return liveData(Dispatchers.Main) {

repository.getRecipeOfTheDay()

.onStart {

emit(RecipeResult.Loading(loading = true))

}

.catch { e ->

emit(RecipeResult.Error(error = e))

}

.collect {

emit(RecipeResult.Loading(loading = false))

emit(RecipeResult.Success(result = it))

}

}

}

The new version of getRecipeOfTheDay() function now has two types of modifications which are consuming the Flow which is coming from SearchRecipeRepositoryImpl and an additional error handling. The function getRecipeOfTheDay() has more responsibility now.

This function is now dealing with Flow, and also uses it’s an operator like onStart, catch, and collect.

Tip: Use onStart{} to perform anything before Flow and other operator gets executed. onStart is the first operator which is triggered whenever Flow starts. On the contrary use onCompletion{} operator to perform anything after Flow finishes its work. onCompletion operator will have an instance of Throwable and will be holding non-null value if the Flow completed with an exception. I tried adding multiple onStart and onCompletion operator in between other operators. And I found if they are used multiple times they will be executed jointly in chronological order.

Each of these operators emits the data of type RecipeResult:

onStart will emit the data of type Loading with value true indicating view to display any loading state.

catch will emit the instance of Throwable wrapped inside Error class so that view can be aware of the cause of the error.

collect will typically emit the Loading and Success on after another to stipulate hiding a loader and displaying the actual data as a successful API call.

After the view model emits the error, it successfully finishes the job, now everything is up to the view class. View class will observe the changes which view model has emitted and takes an action based on the data it receives from the view model. Below code demonstrates how the Fragment will call the function getRecipeOfTheDay from the WhatsNewViewModel class:

whatsNewViewModel.getRecipeOfTheDay()

.observe(viewLifecycleOwner, Observer {

when (it) {

is RecipeResult.Loading -> {

showHideLoader(it.loading)

}

is RecipeResult.Success<*> -> {

showRecipeOfTheDay(it)

}

is RecipeResult.Error<*> -> {

handleError(it)

}

}

})

The observer for this function will receive the RecipeResult type of data and it has a responsibility to unwrap the class and look at what is inside. Here comes the part which I do not like as a software engineer. Ideally view class should not have any decision-making responsibility the business logic should be completely decoupled from the view. Here in this approach since we are bundling multiple types of data under RecipeResult view have to check which data is what. This forced us to write a logic to check the type of data. In the above example, there is a when function is written inside observe {} callback. It is self-explanatory what when function is doing. It delegates the unwrapped result after the type check to dedicated functions to process it further. We mainly care about handleError() function. Since handleError() is part of the view class, it completely up to the view class how it wants to process the error data. Each view class can have different error handling requirements. Here WhatsNewFragment wants to handle only two types of error, that is, NoInternetAvailableException and SomethingWentWrongException. Following code demonstrates how that error can be interpreted and a meaningful message can be displayed to the user:

private fun handleError(it: RecipeResult.Error<*>) {

when (it.error) {

is NoInternetAvailableException -> showError(R.raw.no_internet, R.string.no_internet_available)

else -> showError(R.raw.generic_error, R.string.something_went_wrong)

}

}

private fun showError(@RawRes animation: Int, @StringRes message: Int) {

recipeLoaderView.run {

setLoopingAnimation(animation)

showErrorInSnackbar(message) {

fetchRecipeOfTheDay()

fetchLatestRecipes()

}

}

}

The handleError() performs the check on the type of error instance received from the view model and performs the action accordingly. It further delegates the task of showing error to another private function, that is, showError() which ultimately shows the error to the user with some coll Lottie animation and appropriate message.

Note: The showError() has some more calls to a couple of extension function which uses Lottie animations. Lottie SDK provides an easy way to animate graphics.

The first approach which we just discussed here is doing quite a good job when it comes to the data passing from the view model to view. It is flexible and simple to understand. The only cons here is it gives a view class an overhead to write some more business logic. In Android whenever you put anything on view it restricts you to write a unite test that runs on JVM. This approach makes testing slow.

Approach 2: Using multiple observer and LiveData

Another approach uses the same concept of LiveData and observers. In this approach, we will define multiple, dedicated observers. The view will observe each of these LiveData and accordingly takes action. Imagine there are separate observers for network error, generic errors and to show/hide progress. The view model’s responsibility would be updating the value of the respective observer and emit only and only success data. The view classes, that is, Fragments or Activities will observe the live data and takes an action whenever data is updated. To start using this approach and keeping reusability in mind we are going to abstract out some behavior. To keep it abstract, we need two abstract classes. The classes which we need are ViewStateAwareFragment and ViewStateAwareViewModel. Both the abstract class will hold some abstract behavior and any subclass of type Fragment and ViewModel will implement them. This approach is used for all remaining applications. Check out the classes such as CategoryViewModel, RegionViewModel, SeachRecipeViewModel and RecipeByCategoryFragment, RecipeByRegionFragment, RecipeListFragment and RecipeDetailFragment.

Take a look at the following code and you will have a better idea about what I am trying to explain above:

abstract class ViewStateAwareViewModel : ViewModel() {

private val _networkException = MutableLiveData<NoInternetAvailableException>()

private val _somethingWentWrongException = MutableLiveData<SomethingWentWrongException>()

protected val _progress = MutableLiveData<Boolean>()

val networkException: LiveData<NoInternetAvailableException> = _networkException

val somethingWentWrongException: LiveData<SomethingWentWrongException> = _somethingWentWrongException

val progress: LiveData<Boolean> = _progress

fun handleError(e: Throwable) {

when (e) {

is NoInternetAvailableException -> _networkException.postValue(e)

is SomethingWentWrongException -> _somethingWentWrongException.postValue(e)

}

}

}

abstract class ViewStateAwareFragment : Fragment() {

abstract val progressObserver: Observer<Boolean>

abstract val networkErrorObserver: Observer<NoInternetAvailableException>

abstract val genericErrorObserver: Observer<SomethingWentWrongException>

abstract val viewModel: ViewStateAwareViewModel

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

super.onViewCreated(view, savedInstanceState)

setViewStateObservers()

}

/**

* Sets the observers to respective live data instances.

*/

private fun setViewStateObservers() {

viewModel.run {

progress.observe(viewLifecycleOwner, progressObserver)

networkException.observe(viewLifecycleOwner, networkErrorObserver)

somethingWentWrongException.observe(viewLifecycleOwner, genericErrorObserver)

}

}

}

Two things to discuss further here, check out how these abstract classes are holding the implementation of handleError() and setViewStateObservers(). We have partially defined handleError inside ViewStateAwareViewModel. We have hidden the complexity of how error handling is done if any view model needs error handling capability they just have to extend the ViewStateAwareViewModel class and that’s it, they will get the error handling feature also the subclass does not want to worry about how these error observers are going to work. And along with error handling, showing/hiding a progress loader comes for free.

Tip: The ViewStateAwareViewModel exposes only LiveData<?> and marks MutableLiveData<?> is private, why is that so? It is so because MutableLiveData<?> is mutable and it means this can be changed if observed directly from any view class. So, the class should only expose LiveData<?>, since it is good enough to set observers on it. Liskov substitution principle works here. We should ideally expose base type and let it derive the actual type internally.

So how to use these abstract classes, what child class has to do to leverage this. To get an idea about this, take look at the following codebase. This is the CategoryViewModel which has the function to fetch the list of categories:

class CategoryViewModel(private val repository: SearchRecipeRepository) :

ViewStateAwareViewModel() {

fun getCategories(): LiveData<List<Category>> {

return liveData {

repository.getAllCategories()

.onStart {

_progress.postValue(true)

}

.catch { e -> handleError(e) }

.collect {

_progress.postValue(false)

emit(it)

}

}

}

}

Just like WhatsNewViewModel, the CategoryViewModel is also consuming Flow and uses operator like onStart, catch, and collect. The catch operator will simply call handleError function and support the error handling.

On the other side RecipeByCategoryFragment will subclass ViewStateAware Fragment and provide implementation to networkErrorObserver, genericError Observer, and progressObserver. The view class i.e. RecipeByCategoryFragment is free to provide the implementation of its own choice. Because all these three observers are abstract and need an implementation. Each child class of ViewStateAwareFragment has this responsibility otherwise the program will not compile. The Fragment which implements this abstract class is also a lifecycle owner and instantiates the view model. This means the Fragment will call the function on the view model and sets observer on it. Once the data is received it will update the UI accordingly. And the other observers will also update the UI as and when the change in the live data is observed. In the following code snippet only important aspect of the fragment related to error handling is captured:

class RecipeByCategoryFragment : ViewStateAwareFragment() {

override val viewModel: CategoryViewModel by viewModel()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

super.onViewCreated(view, savedInstanceState)

toolbar.title = getString(R.string.by_category)

showCategories()

}

private fun showCategories() {

viewModel.getCategories()

.observe(viewLifecycleOwner, Observer { categories ->

categoryRecyclerView.apply {

visible()

layoutManager = GridLayoutManager(activity, 2)

adapter = CategoryAdapter(categories)

addItemDecoration(GridDividerItemDecoration(resources.getDimension(R.dimen.sixteen).toInt()))

}

})

}

override val networkErrorObserver = Observer<NoInternetAvailableException> {

categoryLoaderView.showErrorWithAnimation(

R.raw.no_internet,

R.string.no_internet_available

) {

showCategories()

}

}

override val genericErrorObserver = Observer<SomethingWentWrongException> {

categoryLoaderView.showErrorWithAnimation(R.raw.generic_error,R.string.something_went_wrong) {

showCategories()

}

}

override val progressObserver = Observer<Boolean> { progressing ->

if (progressing) {

categoryLoaderView.setLoopingAnimation(R.raw.recipe_loader)

categoryLoaderView.visible()

categoryRecyclerView.gone()

} else {

categoryRecyclerView.visible()

categoryLoaderView.gone()

}

}

}

With this, we achieve error handling in a good way. This approach allows you to write test cases very well and, in those units, test cases you can verify much more, compare to approach 1. In the next chapter, we will take you to the exciting world of unit testing and you will see the difference in these two approaches by yourself.

Write utility using extension function

In Chapter 2: Kotlin Fundamentals under the Extension function section, we learned how useful extension functions are. Extension functions allow you to add up function as if they are part of the API itself. For example, an extension function written over the String class can be accessed via any String instance. No matter where it’s created and where it is referenced. Although we also discovered how extension function statically resolves the function and make it available. Overusing them could cause issues. It is very important we keep its usage to appropriate use cases only. Writing a small utility function is a great use case. While developing an application we have used extension function in many places. In this section, we will cover where we have used extension function in the app and where else they could be possibly used.

The simplest way to show and hide any view

This is possibly the most used and best use case where extension function can be used. Showing and hiding the view is so common in any Android application. Android API does provide a way to show and hide the view, but they were not so straight. As a developer not just me but many in the community felt the same. Showing and hiding the view should be just one function call and nothing more than that. The traditional approach was not so convenient. Following is the traditional way of hiding the view:

button.visibility = View.GONE

But how about if we can hide the view with the following line of code:

button.gone()

This feels much more tidy approach. The above feature can be achieved using the extension function. In the following code snippet, I have shown three types of extension function to show, hide, and removing the view from the screen:

internal fun View.visible() {

this.visibility = View.VISIBLE

}

internal fun View.gone() {

this.visibility = View.GONE

}

internal fun View.invisible() {

this.visibility = View.INVISIBLE

}

Note: The androidx-core-ktx also has a similar feature. This API provides a feature in the form of property and not the function. These properties not just set the visibility but also helps to read the visibility status. For example:

button.isVisible // Returns true if button is visible

button.isVisible=false // Marks button visibility with View.GONE

Formatting string using an extension function

Another use case in our application is formatting a String using an extension function. In the API we sometimes get steps for cooking instructions. This is coming as a single string, but to display on a view it has to be formatted. You cannot use the raw data directly to display on a view. For our application, we would like to show these steps in a numbered bullet list. If I quote the data here is how it looks before and after the processing:

// Before formatting

Bring a large pot of water to a boil. Add kosher salt to the boiling water, then add the pasta. Cook according to the package instructions, about 9 minutes.\r\nIn a large skillet over medium-high heat, add the olive oil and heat until the oil starts to shimmer. Add the garlic and cook, stirring, until fragrant, 1 to 2 minutes. Add the chopped tomatoes, red chile flakes, Italian seasoning and salt and pepper to taste. Bring to a boil and cook for 5 minutes. Remove from the heat and add the chopped basil.\r\nDrain the pasta and add it to the sauce. Garnish with Parmigiano-Reggiano flakes and more basil and serve warm.

// After formatting

1. Bring a large pot of water to a boil.

2. Add kosher salt to the boiling water, then add the pasta.

3. Cook according to the package instructions, about 9 minutes.

4. In a large skillet over medium-high heat, add the olive oil and heat until the oil starts to shimmer.

5. Add the garlic and cook, stirring, until fragrant, 1 to 2 minutes.

6. Add the chopped tomatoes, red chile flakes, Italian seasoning and salt and pepper to taste.

7. Bring to a boil and cook for 5 minutes.

8. Remove from the heat and add the chopped basil.

9. Drain the pasta and add it to the sauce.

10. Garnish with Parmigiano-Reggiano flakes and more basil and serve warm.

Now if you observe the source it has a few special characters. For example, \r\n, etc. These escape characters are nothing but, new line indicator for various OS. We have to manually remove them. But we cannot simply remove them, because wherever they are occurring it’s a hint for us where we want to insert a new line and a numbered bullet.

So, the logic which I have used it to split the string using these special characters as delimiter values and get it in the form of an array. Once we get the error, iterate them using mapIndexed function and transform the array into another final String instance which will be displayed over the UI. The extension function would look like the following:

fun String.formatCookingInstruction(): String {

return this.split(". ", "\r\n", "\r\n\r\n").filter {

it.isNotEmpty() // To remove any empty spaces

}.mapIndexed { index, string ->

// Add numbering followed by cooking instruction and new line character.

"${index.plus(1)}. $string.\n"

}.joinToString("\n")

}

And we have used this extension function straight on the view layer to call it. Check out the below code. Job is done!

cookingStepsTextView.text = cookingInstruction?.formatCookingInstruction()

Checking internet connectivity using an extension function

Before making any API call, we always check for internet connectivity. We haven’t used such a check in our app. We rely on exceptions thrown by Retrofit. The retrofit will throw an IOException if there is no network available. But doing a manual check is also a very famous approach. This can also be a very good use case of using an extension function. In this approach, we write an extension function over the Context class. It has to be the Context class because checking the availability of CONNECTIVITY_SERVICE requires an instance of type Context. The function will return true of the network is available else false.

The extension function for this will look like the following. And it is dead simple:

internal fun Context.isInternetAvailable(): Boolean {

val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager

val activeNetwork: NetworkInfo? = cm?.activeNetworkInfo

return activeNetwork?.isConnectedOrConnecting == true

}

Extension function and InputStream

We all know, how painful reading the InputStream can be. It’s an extremely verbose and most boring piece of code. Kotlin’s standard API makes it very interesting. They provide a very good extension function to read InputStream. But there is a catch. The extension function which is provided in standard API has some variant. For example, BufferedReader can be used line by line or maybe all together. But your application may have the requirement of reading the entire stream in on go and expect the single string to be returned.

Our application has exactly this requirement. And hence we decided to write our extension function on top of the one which is provided in standard API. This extension function has the responsibility to return the entire input stream as a string in one go.

The extension function goes like following:

fun InputStream.readUsingBufferedReader() = this.bufferedReader().use { it.readText() }

If you have a requirement of reading InputStream on multiple places then doing in such a way makes sense. But if you are reading it on only one place or two, it does not make sense writing a wrapper function over the standard one. It can be a bit of over-engineering.

So be cautious about how you use it.

Extension function and higher-order function

While working on the error handling approach we also used a few more Kotlin features here. Those are very tiny yet, very significant changes in the codebase. To the user, we are showing an animated error image along with the snack bar with the error message. To achieve this we are using the amazing higher-order function and super useful extension function features of Kotlin.

The following code will show a snack bar with the message and an action button. When the action button is clicked a function call will happen:

internal fun View.showErrorInSnackbar(@StringRes message: Int, onActionClicked: () -> Unit) {

val snackbar = Snackbar.make(

this,

message,

Snackbar.LENGTH_INDEFINITE

).setAction(R.string.retry) {

onActionClicked.invoke()

}

snackbar.show()

}

The extension function showErrorInSnackbar is written over type View. It takes a message as an argument. The second argument is a simple function which returns nothing, that is, a Unit type. The utility is used to display a snack bar on any of the view’s instance. And any function can be bound with the onActionClicked parameter, which will be called when the user taps on the action button. We utilize the onActionClicked action to call the failing API again, as shown in the below code:

private fun showError(@RawRes animation: Int, @StringRes message: Int) {

recipeLoaderView.run {

setLoopingAnimation(animation)

showErrorInSnackbar(message) {

fetchRecipeOfTheDay()

fetchLatestRecipes()

}

}

}

The recipeLoaderView is of a view type that calls showErrorInSnackbar with the message parameter. A second parameter onActionClicked can be written as a lambda function which calls the API for the latest recipe and recipe of the day by calling two functions simultaneously.

Implicit intent and extension function

One better example of extension function you can see in the application is, managing the implicit intents. Implicit intent means an intent will find out what activity it has to open. Here we do not specify the destination activities name and its class reference. We just specify the criteria and Android OS will find out a suitable activity for us.

In AndroidManifest.xml file we provide intent filters, this intent filters can be used to find out single or multiple activities based on the action and category or data you are providing via intent. In our application, we have a requirement of opening a browser or sharing the recipe content. This can be easily achieved with the help of implicit intent. Implicit intent is one of the oldest features which we have since the early days of Android development.

Let’s write a function that opens a URL in a browser. And the function will return true if it’s able to open the browser else return false. We have taken care of a situation where implicit intent is not able to open the browser, hence, in this case, we will return false. Android OS will throw ActivityNotFoundException if the intent is not handled by any of the Activity. So we will catch the ActivityNotFoundException and return false if thrown. Next code snippet is about such function:

internal fun Context.openInBrowser(url: String): Boolean {

try {

val intent = Intent(Intent.ACTION_VIEW)

intent.data = Uri.parse(url)

startActivity(intent)

return true

} catch (e: ActivityNotFoundException) {

e.printStackTrace()

return false

}

}

This a case of an extension function for class Context. Why we chose Context and not Activity or Fragment? Because Context can be available for both Fragment and Activity. All activities are an indirect child of Context, and Fragment cannot be possible without an Activity. Each Fragment has access to its parent Activity. So doing this way makes perfect sense.

So how would Fragment will can use this function? Well, there is one tinier wrapper function we have written on top of this function, which is the following:

fun Fragment.openInBrowser(url: String) = requireActivity().openInBrowser(url)

The example usage of this function invocation can be seen inside RecipeDetailFragment.kt class. Also in the same class, we have used one more extension function to share the content of any recipe. This function is called shareIntent().

The shareIntent() function takes a text as a parameter and shows all possible application which can show that content. For example, Messaging, WhatsApp, Email, Messenger, etc. Check out the following code which does the same. The approach is similar to the openInBrowser() function:

internal fun Context.shareIntent(text: String, subject: String = ""): Boolean {

try {

val intent = Intent(android.content.Intent.ACTION_SEND)

intent.type = "text/plain"

intent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject)

intent.putExtra(android.content.Intent.EXTRA_TEXT, text)

startActivity(Intent.createChooser(intent, null))

return true

} catch (e: ActivityNotFoundException) {

e.printStackTrace()

return false

}

}

fun Fragment.shareIntent(text: String, subject: String = "") = requireActivity().shareIntent(text, subject)

Using KTX and Splitties

The extension function became popular because Android loves ceremony functions. And in past days there is no easy and clean way to deal with them. For example, dealing with SharedPreference means, dealing with SharedPreference. Editor, also the commit() and apply() API. To manage Fragments and the back stack there is also a lot of ceremony calls, where you get the instance of fragment manager, and do begin the transaction, add them to back stack and at last call commit. These series of call is known as ceremony of function as demonstrated below:

getSupportManager()

.beginTransaction()

.replace(R.id.container, fragment)

.addToBackStack(null)

.commit();

This problem is very well addressed by KTX and Splitties. What are KTX and Splitties? They are a collection of extension functions written for various Android core APIs, for example, Context, Activity, AlertDialog, View, Animation, Fragment, etc. There are loads of utility function written inside. KTX is an official set of APIs written and maintained by the Google team itself. They still do not have all the features which we want in our daily development. There was a very good library from the JetBrains named Anko, but since Android is coming up more and more new APIs and their extension libraries, so they decided to deprecate the Anko. When I was writing this chapter, the Anko library became deprecated in the month of Dec 2019. Splitties could be an alternative to Anko, but many of its releases is in alpha. Alpha releases have their own cons:

Note: Splitties is a third party library, written and maintained by open source community. They provide a very good set of utility function which can make your day to day development very easy. Find more about APIs and features at https://github.com/LouisCAD/Splitties

We have used KTX and Splitties on abundant places. If you take a look at the app.gradle.kts you will find below libraries which we are using in our application development:

implementation("androidx.core:core-ktx:1.1.0")

implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion")

implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-rc02")

implementation("androidx.navigation:navigation-fragment-ktx:$navVersion")

implementation("androidx.navigation:navigation-ui-ktx:$navVersion")

implementation("androidx.navigation:navigation-fragment-ktx:$navVersion")

implementation("androidx.navigation:navigation-ui-ktx:$navVersion")

Most of the APIs which we have used is, because of the usage of the navigation component. These navigation component dependencies are mostly used for internal purposes. We have used only two functions from these API. The function which we have used is binding the BottomNavigationView to the series of fragments. Remember the following code which we used to display all three fragments and bind it to the instance of BottomNavigationView?

bottomNavigationView.setupWithNavController(findNavController(R.id.navHostFragment))

The support for LiveData which is compatible with coroutine is also nothing but an extension function sued from livedata-ktx library. We have used it in all view models. Following is the gist from the RecipeListViewModel.kt file:

fun getRecipeByName(recipeName: String): LiveData<List<Recipe>> {

return liveData {

repository.searchRecipeByName(recipeName)

.onStart {

_progress.postValue(true)

}

.catch { e -> handleError(e) }

.collect {

_progress.postValue(false)

emit(it)

}

}

}

On the other hand, Splitties is also full of rich utility functions which are not particularly related to any concept like ViewModel, LiveData, or Coroutine. But they are very generic and helps you to achieve small functionality very quickly. For example, Showing toast messages or alert dialogues. It also has some nice utility function to deal with PendingIntent, Collection, Bundle, permission handling, RecyclerView, etc.

We have used Splitties to display alert dialogues. They have nice DSL and have a lot of resemblance to Anko APIs. Following code helps to display an alert dialog using Splitties:

requireContext().alertDialog {

messageResource = R.string.video_not_found

okButton()

}.show()

And the above code will show the same native alert dialog which AlertDialog.Builder API creates for Android. It’s just syntactic sugar.

Convert build.gradle from Groovy to Kotlin DSL

At this point in the application, we have written pretty much all of the code which is required to show a meal recipe to the user. So can we say our application is truly Kotlin-ized now? Have we used Kotlin in all possible places? Or there is still some opportunity to improve? Android build system uses Gradle as a tool. And the Gradle tool has an essential Metadata file named build.gradle. Building an Android project highly relies on this file. And as an Android developer, you might be knowing that there are two such files for any standard single module project. One file lives on a project level and the other is on the app level. These two files use Groovy as a language. Is there any possibility of using Kotlin here? Yes, it is. You demand and Kotlin team will bring the best on the table. They have come up with a way to let developers write build.gradle files using Kotlin.

Why one would ditch Groovy?

You might have thought about, not to ditch groovy, because whatever it is, it works fine for you. Your project is compiling and building very fine. You already have a lot of releases on production. Why would you care about converting from Groovy to Kotlin DSL for maintaining these two files? The questions are fair and valid. But I would still like to point out a few pros and cons of the new approach of writing these Gradle files using Kotlin DSL.

Pros of using Kotlin DSL

DSL is statically typed and type-safe, while Groovy is not. It’s a dynamically typed language from Apache.

Kotlin DSL has extraordinary IDE support. IntelliJ IDEA and it’s sub product Android Studio fully support Kotlin. You get very good IDE support when it comes to auto-completion and better error messaging. While for Groovy this may not be true. Not all IDE supports Groovy fully and this leave developer fiddle around and they don’t know what is going wrong until they try to build the project.

DSL is readable. Although Groovy is also much more readable.

Debugging support is very good. For Groovy the debugging experience can be horrible.

Cons of using Kotlin DSL

Very less documentation. Documentation is something I consider a lot before I start using anything. This approach is still fairly new. Not all developers use this in their application.

It increases build time slightly. The logical reason is once you convert your groovy code into Kotlin DSL, it increases the build time relatively. After I convert the Groovy code into Kotlin DSL it roughly increases the build time by 5-10 seconds variably. Our application is relatively pretty much small compared to real-world applications. And for a bigger project, this could be a great concern.

There is no guide to use Kotlin DSL for Gradle. All you find on the internet is people have learned from trial and error and other’s experience.

But I am sure over the period of time the Kotlin team will improve build time a lot. Please go through this section with an open mind and attitude to learn something new, or skip to next chapter happily.

If you are reading this line, that means you have decided to be different and chose a path that will take you on the journey where Groovy impressions will be removed from Gradle files and Kotlin DSL will rise. This is going to be a step by step activity, that is:

Updating the Gradle distribution version.

Getting rid of all single quotes (').

Changing Groovy function calls

Updating settings.gradle.

Updating project level build.gradle.

Updating app level build.gradle.

Step 1: Updating the Gradle distribution version

The first step we do is changing our Gradle distribution version. This may not require in most of the case. I would highly recommend to upgrade it to the latest one. At the time of writing this, I am using Gradle version 3.4.1 and Gradle distribution 5.1.1. Change the distributionUrl inside gradle-wrapper.properties:

distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip

Using a Gradle distribution 5.0 and above is only and only recommended if you are using Gradle version 3.4 and above.

Step 2: Getting rid of all single quotes (')

There are mainly three .gradle files which we are targetting. And we need to convert these three files from Groovy to Kotlin DSL. There three Gradle files are using single quotes in various places. We need to change all single quotes (') to double quotes ("):

Note: Before you perform this step, make sure you turn off, automatic Gradle sync. You will go mad if you try to sync your Gradle files with every change. I will instruct when and where you should sync your Gradle files. Look for * sign in this section. * sign indicates that step includes Gradle sync.

Check out the following examples where we have explained how this change will affect this three Gradle file:

include ':app'

// will become

include ":app"

implementation fileTree(include: ['*.jar'], dir: 'libs')

// will become

implementation fileTree(include: ["*.jar"], dir: "libs")

classpath 'com.android.tools.build:gradle:3.4.1'

// will become

classpath "com.android.tools.build:gradle:3.4.1"

implementation 'androidx.appcompat:appcompat:1.1.0'

implementation 'androidx.core:core-ktx:1.1.0'

// will become

implementation "androidx.appcompat:appcompat:1.1.0"

implementation "androidx.core:core-ktx:1.1.0"

*After you replace all single quotes with double quotes, it’s time to sync the Gradle. Click on Sync now or File/Sync Project with Gradle Files.

Step 3: Changing Groovy function calls

Alright at this stage we have upgraded the Gradle version and removed all single quotes. Now Kotlin has the concept of properties and functions. Groovy also has variable declarations and functions. But the syntax is very different. Groovy is a bit ambiguous when it comes to property and function declaration. They may seem very similar to the first look. For example, the following code has versionName and implementation. VersionName is a property or you can say a variable. But implementation is a function. For the newbie it is impossible to figure out the difference:

versionName "1.0"

implementation "com.github.bumptech.glide:glide:4.9.0"

Kotlin DSL is highly declarative and it removes this ambiguity with a proper syntax for property and function calls. So in this section, we will update all properties and function call with its appropriate syntax. The preceding code highlights some of the transformations we have made from Groovy to Kotlin:

// Groovy

applicationId "com.hardiktrivedi.theinternationaldhaba"

versionCode 1

versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

// Kotlin

applicationId = "com.hardiktrivedi.theinternationaldhaba"

versionCode = 1

versionName = "1.0"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

// Groovy

minSdkVersion 23

targetSdkVersion 29

def lottie_version = "3.3.1"

implementation "com.airbnb.android:lottie:$lottie_version"

// Kotlin

minSdkVersion(23)

targetSdkVersion(29)

val lottieVersion = "3.3.1"

implementation("com.airbnb.android:lottie:$lottieVersion")

// settings.gradle with implicit function call

include ":app"

// settings.gradle with explicit function call

include(":app")

This is going to be a very crucial step. Please do this step and repeat it for all required files with extra caution. The property call is separated by = operator and function call uses typical parentheses. At the end of this section your, all implicit function call should use explicit function call syntax using parenthesis. There is a caveat here. A lot of developers have a habit of using because while declaring Gradle dependencies. And when you explicitly specify function calls, you will end up in this situation where you have to think about this because of the statement. Kotlin can deal with this situation as well. Following code can explain to you how you handle this in Kotlin:

def lottie_version = "3.3.1"

implementation ("com.airbnb.android:lottie:$lottieVersion") {

because ("Lottie animations are used to show animated progress loaders")

}

val lottieVersion = "3.3.1"

implementation("com.airbnb.android:lottie:$lottieVersion") {

because("Lottie animations are used to show animated progress loaders")

}

It looks like it just supports it naturally. Because it is a function we usually explicitly write in our .gradle file. Kotlin’s function syntax supports it.

Tip: Many times, we use third-party libraries to speed up our development. But it’s not at all necessary that all your fellow developers know about it. In these circumstances, it is better we use because the function to explain what your Gradle dependency is doing.

Also if you notice while you convert Groovy properties and functions into Kotlin compatible functions, we should change all your defined variables into Kotlins vals. An example of such conversion is shown below:

// Groovy

def retrofit_version = "2.6.2"

// Kotlin

val retrofitVersion = "2.6.2"

Note that before we were using _ to separate two different words, but Kotlin uses camel case to do that. We have to respect language conventions, hence we replace all _ with came case in our Gradle files.

Up until this, we have completed all the preparatory steps. And now our Gradle files are ready to be converted. Let’s proceed to step 4.

Step 4: Updating settings.gradle

After all the hard work, things should be a little less tricky then the expectation. At step 4 we should target the smallest .gradle file. The settings.gradle is tiny, it includes your module name. Now let’s rename settings.gradle to settings.gradle.kts. To rename most appropriately, right-click on settings.gradle file. Choose Refactor/Rename option. A pop-up window will appear as shown in Figure 7.4 and you can enter the new file name i.e. settings.gradle.kts.

Tip: IntelliJ IDEA provides a great list of a shortcut which you can customize as well. To refactor anything just press Function+Shift+6. You can refactor classes, function and object’s name. Also, you can rename File names throughout the project and its references. For example, if you rename Activity’s class name, the refactor will also rename it in AndroidManifest.xml file and all over the places where it is referenced. Make sure you check/uncheck Search for references option in the dialog box as shown in Figure 7.4. You can also change the keymap for your shortcut from Preferences/Keymap

Figure 7.4

* There is a caveat here as well. In reality, our application is a multi-module. It never consists of only and app module. How do I support multi-module when my settings.gradle file is converted into Kotlin DSL. In Kotlin include function accept the variable arguments. So with this reasoning, you can pass multiple module names separated by comma (,) and all of that module will be included. So your application can support multiple modules inside settings.gradle.kts file as shown in the below example. And click Sync now:

include("app","profile","notification","coremodule")

Step 5: Updating project level build.gradle

Let’s take another leap forward and update the project level build.gradle. This Gradle file consists mainly of the classpath. It also consists of another key information i.e. location of the repository which provides you the dependencies. For example, jcenter, mavencentral, etc.

* Provided you have already taken care of using explicit function call and property declaration as per steps 2 and 3. This should be just a matter of renaming the file. Rename the file as per the shortcut explained in step 4. Rename your project level build.gradle to build.gradle.kts. And click Sync now. If the sync is not successful there can be the following reasons.

Maybe you are missing some of the property declaration and function call conversions. Take a look at the below sample code and see if you have provided similar declarations in your file:

buildscript {

val KotlinVersion = "1.3.31"

val navVersion = "2.1.0"

repositories {

google()

jcenter()

gradlePluginPortal()

}

dependencies {

classpath("com.android.tools.build:gradle:3.4.1")

classpath("org.jetbrains.Kotlin:Kotlin-gradle-plugin:$KotlinVersion")

classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$navVersion")

// NOTE: Do not place your application dependencies here; they belong

// in the individual module build.gradle files

}

}

Another reason could be your custom Gradle tasks. Those custom classes need to be converted into Kotlin DSL as well. Take a look at the below example:

tasks {

val clean by registering(Delete::class) {

delete(buildDir)

}

}

It’s a combination of DSL and delegated properties. Clean is a property delegated to registering function? And buildDir is nothing but a function defined inside Project.java.

Try performing Gradle sync again and hopefully it should work.

Step 6: Updating app-level build.gradle

After step 5 is a success, here we are at our last step of converting. This is the most crucial Gradle file. It contains so much information about the application. This file won’t be as simple as Step 4 and 5. Inside this step 6, we will be performing a lot of little changes and achieve the bigger goal step by step.

The first action would be renaming the file from build.gradle to build.gradle.kts.

Next task is to add the import statements. On top of your build.gradle.kts file add below two import statements:

import org.jetbrains.Kotlin.config.KotlinCompilerVersion

import org.jetbrains.Kotlin.gradle.dsl.KotlinJvmOptions

These two import statements has few constants declaration inside. For example:

// KotlinCompilerVersion

KotlinCompilerVersion.VERSION // Includes latest Kotlin version

isPreRelease() // If the version is a pre release or a stable version

// Inside KotlinJvmOptions interface

jdkHome // Path to JDK home directory

jvmTarget // Target version of the generated JVM bytecode (1.6, 1.8, 9, 10, 11 or 12), default is 1.6

After understanding the import statements, it’s time to change the plugins. For Kotlin DSL plugins is a function that has few nested calls to a couple of functions that loads the Android plugin, Kotlin plugin, and safeargs plugin. Have look at the below code snippet:

plugins {

id("com.android.application")

Kotlin("android")

Kotlin("android.extensions")

id("androidx.navigation.safeargs.Kotlin")

}

For all Kotlin specific plugins, you only use Kotlin function to load the plugins.

After this change, it’s time to change what goes inside the Android block. Android blocks contain a lot of information such as compile version, minimum SDK version, build types, Kotlin options, etc.

The buildTypes will ask for some special treatment. Here we specify the release, debug build types, proguard files declarations. The complete android block is as follows:

android {

compileSdkVersion(29)

defaultConfig {

applicationId = "com.hardiktrivedi.theinternationaldhaba"

minSdkVersion(23)

targetSdkVersion(29)

versionCode = 1

versionName = "1.0"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

}

buildTypes {

getByName("release") {

isMinifyEnabled = false

proguardFiles(

getDefaultProguardFile("proguard-android-optimize.txt"),

"proguard-rules.pro"

)

}

}

KotlinOptions {

val options = this as KotlinJvmOptions

options.jvmTarget = "1.8"

}

}

The remaining changes you can verify in the actual source code.

* Hit a Sync now and sync all Gradle files for the project and expect your project to build.

For this step, I mainly faced problem while dealing with buildTypes, isMinifyEnabled for proguard changes. For them, you need to find how they are written. This took some time to find out how it can be done.

My experience is, the entire conversion task is not as smooth as I was expecting.

Converting the Gradle file from Groovy to Kotlin DSL has not got much attention and love from the community.

I was struggling to get the answer to the few conversion.

There is no clear documentation.

All I did was trial and error based on my understanding and guessing work.

This has to be a very cautious step. You will need a lot of assistance and help. The bigger your Gradle files are, the bigger are the problems. This reminded me of a quote of a Robert Tew The struggle you're in today is developing the strength you need for tomorrow.

Wrapping up

Here comes one more exciting journey and learning experience comes to an end. We learned a lot of new things as a part of this chapter. We learned, how refactoring is important and adds value to your application. Refactoring helps to scale up your application. This small contribution makes a huge difference in the future.

We added some much Kotlin specific changes and give a touch of functional programming aspects. In this chapter, we moved from a pure objected oriented approach to a functional programming approach.

Converting build.gradle was a bit tricky and difficult but it was a fantastic experience.

In the next chapter now we are moving towards the most important aspect of software development. We did not pay much attention to testing, but the next chapter is all about testing. We will learn how we can add lots of test cases and secure our code and business logic. We will be learning exciting jUnit 5 feature, we will use DSL again and write testing robots. Also, we get answers about how mocking can be done in Kotlin. Stay tuned!

CHAPTER 8

Testing the Kotlin Code

“Why do most developers fear to make continuous changes to their code? They are afraid they'll break it! Why are they afraid they'll break it? Because they don't have tests.”

― Robert C. Martin

There are hundreds of popular quotes about software testing, or writing unit tests. All those make so much sense and shows the significance of writing unit tests. If you are writing code you must write a test to protect your code. A well written and commented code can be great documentation for your fellow developers. Having said that, a well written unit test explains a lot to your fellow developers. One can confidently modify the code without being worried about introducing more bugs. Well written test cases can be a great source of truth. Because whatever test says, is on production and that's what your customer is getting.

In this chapter we will learn how Android professionals write various types of tests, to protect their code. A lot of interesting things have evolved around writing test cases. Companies who bother about the engineering aspect of the product, they don't even deliver the feature to the customer without writing test cases for that:

Structure

This chapter covers the following topics:

Software testing pyramid

Writing unit tests using JUnit5

Unit testing coroutines and live data

Best practices for writing unit tests

Application testing approach

So how one should test Android applications? Over the period of time, there is a revolutionary framework developed and introduced to the developer community. At the same time, there is still some core framework that is still in use. In this section, we will learn what should be a possible approach to test your android application.

The approach we will be discussing here is recommended by some of the great industry leaders. This approach is widely used in many organizations. Android team has suggested one testing pyramid to explain how you should arrange your test cases. Before we deep dive into the approach, let's take a step back and ask a question to ourselves, i.e. why I should write tests?

Why I should care about tests?

Your company may have a dedicated team for quality assurance. Their job is to find bugs and malfunctions in the app. So, if your organization has such a great team, why you would test on their behalf? Why you would increase your work?

Quality of code

Do you like apps that are buggy? Do you curse the product team and their staff when some of the apps which you use do not work properly? I am sure you do. The same thing may happen with you if you deliver a buggy code and the customer finds a problem while using your application. It's your responsibility to deliver code which is great in quality. It improves quality to a great extent.

Makes refactoring easy

Read the uncle Bob's quote at the beginning of the chapter. Have you ever felt the same? If you have worked on a legacy code ever in your life, you can easily relate yourself to that quote. If there are tests in your application, your fellow developer can refer it as a source of truth and make sure whatever changes he does, would not break any existing functionality. He can confidently perform the refactoring.

Find bugs during development

If you perform a good amount of refactoring, there could be chances you are introducing bugs. Your test suit can help you to find bugs on a much early stage. Before you make your features available to QA you can easily get to know if your code has affected any functionality or module. And this makes sure that your changes do not hamper someone's code and ultimately application's quality.

Great cost saver

It can be a great cost saver for your project. When I say about the cost it's not just money. It is also the time. Since having test cases allows the possibility of refactoring and improving the code and even enhancing features, it can save you a lot of time since you don't have to perform the manual task of checking everything in your app. Your test cases will tell you whether your application is working as per expectation or not. During the release of the app, if you hotfix any issue, the entire build generation, and regression testing cycle becomes fast and reliable. You can go on production without much of a faff.

Enforce good structural code

If you decide to write the test case, it will affect the way you structure and the way you think the code. If you preach yourself, a test-driven development you will make sure each line of code is testable. And this will enforce you to come up with great design and architecture. You will make sure how each line of code can be tested in isolation.

Testing Pyramid

So how would you achieve the above points, how would you start writing an awesome test for your application? Google recommends the below approach as described in Figure 8.1:

Figure 8.1

The diagram is self-explanatory. It is divided into three layers.

Small tests

Your unit tests, which run in isolation. Unit tests run on JVM and provide you very high fidelity. They also run very fast.

Medium tests

Medium tests are mostly integration tests. Here you can verify case like, Activity is launched with required intent parameters. Can use navigate from one fragment to another fragment? You can test integration between modules, etc.

Large tests

End to end test cases is written to validate the entire feature. For example, the user is able to see the latest recipes and able to navigate to recipe detail.

On any tech stack when you think of adding test cases, you should start adding small test cases. This is the best way to start writing tests. I belong to Gujarat, a quite business-oriented community from the western part of India. We have a proverb, i.e. Teepe teepe sarovar bharay. This means every drop of water counts when it comes to the feeling pond. And its literal English proverb is Rome was not built in a day. So, with this approach, let's add some unit tests for our code.

As per our application architecture, below should be a candidate for unit tests.

Repository classes

View models

Any extension function which does not use any Android APIs

Writing unit tests using JUnit5

Let's write the unit test in a new way. The traditional way of writing a unit test is using JUnit4. JUnit4 is a great framework that will allow developers to assert what they are expecting from the piece of code. Writing testable code is an art. A unit here is a piece of code that can run independently and in isolation. These two factors offer fidelity and faster execution time to your unit tests. But tests are not readable. It takes time and practice to write readable and meaningful tests. The new JUnit5 is a mixed flavor of three different sub-projects.

JUnit5 combines core JUnit platform, JUnit Jupiter, and JUnit vintage:

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

Features of JUnit5

Let's spare a few minutes to take a look at the JUnit features. We won't use it just because it sounds and looks cool.

Rich set of annotations

Plenty of annotations to command this testing framework. You can say a lot by just using one word in the form of annotation. JUnit5 offers a very rich set of annotations. To run JUnit5 you require Java8 or above. Android uses Java8, so we can now use the JUnit as it any Android project which is compiled by Java8 meets this basic requirement:

@ParameterizedTest: Denotes that a method is a parameterized test. Such methods are inherited unless they are overridden.

@TestMethodOrder: You can give your preference in order to run test cases. This annotation can also inherit. This is similar to @FixMethodOrder.

@DisplayName: You can declare your custom names. You may have faced a situation where you end up giving the long name to functions to make it readable in the test report. With @DisplayName annotation, you can describe whatever you want as name and in test report, you will see the newly given name.

@Disabled: Similar to @Ignore of JUnit4. With this annotation, the platform ignores this test case and doesn't run it.

DisplayName

It would be unfair if we don't discuss this special annotation in detail. This is one of the coolest features which I like. As explained earlier, you can describe your test cases in the way you want. It supports even special characters and emojis:

@Test

@DisplayName("Then NoRecipeOfTheDayFoundException is thrown 😱")

fun ‘NoRecipeOfTheDayFoundExceptionIsThrown’(){

}

With clever usage of DisplayName along with features like the nested class, you can come up with very good grouping and naming conventions. We have used this feature a lot in our test cases. You will see when we cover this while writing test cases.

Grouped assertions

Have you ever ended up writing a series of statements where you are simultaneously asserting on a single object? With JUnit5 you can group your assertions and this performs much readable code. This makes assertions very clear on the first glance itself:

@Test

void personObjectIsInitializedAppropriately(){

Person person = new Person("Hardik Trivedi", "Software engineer");

assertEquals("Hardik Trivedi",person.getName());

assertEquals("Software engineer",person.getProfession());

}

// Better solution with JUnit5

@Test

void personObjectIsInitializedAppropriately(){

Person person = new Person("Hardik Trivedi", "Software engineer");

assertAll("person",

() -> assertEquals("Hardik Trivedi", person.getName()),

() -> assertEquals("Software engineer", person.getProfession())

);

}

This is great. Above is a Java code though. I haven't tried how this feature works with Kotlin. It would be a good idea to try with Kotlin. Kotlin offers its syntax of writing lambda. I checked bit more what could be done and JUnit5 documentation also explains how this feature can be used in Kotlin. Check out the following code how this feature can be used in Kotlin:

@Test

fun ‘grouped assertions’() {

assertAll("Person properties",

{ assertEquals("Hardik Trivedi", person.name) },

{ assertEquals("Software engineer", person.profession) }

)

}

Asserting exceptions

JUnit4 did provide a way to assert the exception, but to me, it never sounds clear. You have to mention explicitly above the test function signature. JUnit5 came up with much better way of asserting any type of exceptions. Below code snippet will give you an idea of how clear it is to understand that we are asserting the Exception of type NoRecipeOfTheDayFoundException:

@Test

@DisplayName("Then NoRecipeOfTheDayFoundException is thrown")

fun ‘then NoRecipeOfTheDayFoundException is thrown’() {

val exception = assertThrows<Exception> {

runBlockingTest {

advanceTimeBy(SearchRecipeRepositoryImpl.delay)

repository.getRecipeOfTheDay().first()

}

}

Assert.assertTrue(exception is NoRecipeOfTheDayFoundException)

}

There are lot of such features we can talk about for entire chapter. But we will now take a look in the actual example and implementation we did in our application.

Configure JUnit5 for Android app

All android application by default has Gradle dependency for JUnit4. On top of this dependency, we will add JUnit5 related dependencies as described below:

//Inside project level build.gradle.kts file

classpath("de.mannodermaus.gradle.plugins:android-junit5:1.5.2.0")

//Inside app level build.gradle.kts

plugins {

// Other plugins

id("de.mannodermaus.android-junit5")

}

val junit5 = "5.5.2"

// (Required) Writing and executing Unit Tests on the JUnit Platform

testImplementation("org.junit.jupiter:junit-jupiter-api:$junit5")

testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$junit5")

// (Optional) If you need "Parameterized Tests"

testImplementation("org.junit.jupiter:junit-jupiter-params:$junit5")

Sync your Gradle files now. After a successful sync you are ready to go. As per our approach, we will be writing unit test cases for our repository layer.

Unit test your repository layer

The sole responsibility of repository layer is to bring the data from network call and remove the chaos from raw data. Our repository class has long list of duties. For this section we will focus on only one function call i.e. recipeOfTheDay(). This function has lot to test. Take a look at the below code once again and figure out what all test cases are required:

override suspend fun getRecipeOfTheDay(): Flow<Recipe> {

return flowOfRecipe { searchRecipeService.getRecipeOfTheDay() }

.onEach { delay(delay) }

.map { it.meals }

.map { it.first() }

.catch { e -> transformError(e) }

.map { it.mapToRecipe() }

}

I have compiled following list of possible test cases, to secure above function completely:

Assert that recipe is fetched and mapped to type Recipe

Assert that if no recipe is found, it throws NoRecipeFoundException

Assert that if no internet is available, it throws NoInternetAvailable Exception

Assert that if any generic error occurs, it throws SomethingWentWrong Exception

Once you decide what you want to test, each assertion criteria becomes a test case for you. While we achieve above all four criteria, we also need to face some of the challenges. getRecipeOfTheDay function is using suspend-able coroutine. It has a delay function call inside. And most importantly, it has an API call. While running test cases obviously you don't want to perform API call. That would be the most unintelligent decision ever. We will never be such a brainless. Network calls cost you money, time, and stability. When you are testing, you should be as isolated as you can with such external factors.

Unit test suspend function

Since every function inside repository is a suspend-able function, it requires special context. The suspend-able function cannot be called just like any other function. To learn how to unit test a coroutine code, you need to add following Gradle dependency in your application level Gradle file:

val coroutineVersion = "1.3.2"

testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutineVersion")

This dependency is officially written by the Kotlin team to provide ease and a definite solution for your test cases. The library has plenty of utility function which you can use while writing test for your coroutine code. Let's start writing our first test case for the repository class.

What we want to assert is… Given user wants to see recipe of the day, when internet is available, then recipes are fetched. The test for this would look like following code:

@Test

fun ‘recipe of the day is fetched successfully’() {

runBlockingTest {

advanceTimeBy(SearchRecipeRepositoryImpl.delay)

val recipe = repository.getRecipeOfTheDay().first()

assertEquals(recipe, com.hardiktrivedi.theinternationaldhaba.recipe)

}

}

Any coroutine test can be written inside runBlockingTest function. You can think of this as runBlocking, but pass async and other delays immediately. It runs the test body with EmptyCoroutineContext. The test body can be any function which is marked with suspend keyword.

The class under testing here is SearchRecipeRepositoryImpl, so in the setup function, we will instantiate this will all required dependencies. This class is particularly interested in SearchRecipeService. The SearchRecipeService can be easily mocked. We have used mockk (https://github.com/mockk/mockk) to mock various types and behavior.

Note: There are various libraries available which can be used to mock your dependencies. E.g. Mockito (https://site.mockito.org/) is very well defined and widely used library to mock the dependencies. Similarly, mockito-kotlin from Niek Haarman is very nicely written lib. This library is very much recommended and very much loved by developer community. The author is keeping it very up to date. Kluent (https://markusamshove.github.io/Kluent/Mocking.html) is another smart wrapper on mockito-kotlin. Purpose of Kluent is to provide fluent assertions. There is no verbosity like other mocking framework.

If your test function has delay function in it, your test cases will consider that and execution time will be affected. In your actual code, you will not have such requirement. But in case you end up having delay in your any coroutine code other than your repository, you can use advanceTimeBy() function. It internally moves clock of your coroutine context by given time and helps you to speedily execute your test.

Using inner classes to group your test cases

Our repository classes have long list of test cases. There are total 8 functions and they are covered by roughly 16 test cases. It could have been more. We have skipped few scenarios which were repetitive for other functions. With the feature of inner class combined with power of JUnit5. With the help of inner class, we can group our test cases. For example, the repository class can be grouped by either per function or like, what if internet is not available, or if something went wrong. How you want to group the test cases is totally on you.

Our idea is to group them feature wise and within that we will group them in two sub groups:

When network is connected

When network is not connected

To give you an idea following could be the structure of our repository test class:

internal class SearchRecipeRepositoryImplTest {

// Mocking and other initializations

@BeforeEach

fun setup() {

MockKAnnotations.init(this, relaxUnitFun = true)

repository = SearchRecipeRepositoryImpl(searchRecipeService)

}

@Nested

@DisplayName("Given user wants to see recipe of the day")

inner class RecipeOfTheDay {

@BeforeEach

fun setup() {

coEvery { searchRecipeService.getRecipeOfTheDay() } returns recipeListResponse

}

@Nested

@DisplayName("When network is connected")

inner class WhenNetworkConnected {

@Test

@DisplayName("Then recipe of the day is fetched successfully")

fun ‘recipe of the day is fetched successfully’() {/********/}

@Test

@DisplayName("Then NoRecipeOfTheDayFoundException is thrown")

fun ‘then NoRecipeOfTheDayFoundException is thrown’() {/********/}

@Test

@DisplayName("Then SomethingWentWrongException is thrown")

fun ‘then SomethingWentWrongException is thrown’() {/********/}

}

}

@Nested

@DisplayName("Given no network is available")

inner class NoNetworkAvailable {

@Test

@DisplayName("when trying to fetch categories, then NoInternetAvailableException is thrown ")

fun ‘fetch categories throws NoInternetAvailableException’() {/********/}

@Test

@DisplayName("when trying to fetch all regions, then NoInternetAvailableException is thrown ")

fun ‘fetch all regions throws NoInternetAvailableException’() {/********/}

@Test

@DisplayName("when trying to fetch recipe by name, then NoInternetAvailableException is thrown ")

fun ‘fetch recipe by name throws NoInternetAvailableException’() {/********/}

}

}

I have omitted the implementation of each function here, because we are focusing on class structure and also how @DisplayName can be used to form group and it's naming convention. We have formed the Given, When, Then convention if you see here. Take a look at the Figure 8.2 to see how the output would look like:

Figure 8.2

Unit testing ViewModel and LiveData

Testing a ViewModel can be entirely different than testing repository layer. Both have entirely contrast in their responsibility. Repository layer connects to the network and brings you a raw data. There you test various network and data parsing related scenarios. But ViewModel talks to view layer. They receive data from repository layer and polish the data before it reaches to the final view layer. Developer community uses different techniques to perform such tasks. Android team recommends using a LiveData here. LiveData is basically concept of observer and observables. There is also a clear way to unit test view model layer.

We have selected RecipeListViewModel as an example to explain about adding unit tests. RecipeListViewModel list the recipes based on category, region, or its name. There are different functions written for that. The only fundamental thing those functions are doing is as follows:

Getting data from repository

Handling error

Managing loaders

It's the pattern and in same pattern all three functions are behaving. Take a good look at the following function and understand the pattern:

fun getRecipeByCategory(categoryName: String): LiveData<List<Recipe>> {

return liveData {

repository.getRecipeByCategory(categoryName)

.onStart {

_progress.postValue(true)

}

.map { it.map { it.mapToRecipe() } }

.catch { e -> handleError(e) }

.collect {

_progress.postValue(false)

emit(it)

}

}

}

To unit test this we have following possible assertion cases considering internet is connected and no error occurs:

Loader becomes visible

Loader becomes invisible

And Data is emitted

As an approach we will follow the following:

Mock repository layer.

Mock observers to assert that observer will respond over the changes happen when data is emitted.

Create separate mock observers for asserting loading states and data.

Changes in the observers should respect the order of data emission. For example, loader should be visible first and hide after that. At the end data should display.

The test case would look something like following code:

@Test

@DisplayName("Then recipe by category successfully retrieved with correct loaders")

fun ‘retrieves recipe by category’() {

coEvery { repository.getRecipeByCategory(any()) } returns flowOf(

recipeByCategoryRegionOrNameResponse

)

testDispatcher.runBlockingTest {

viewModel.run {

progress.observeForever(loaderObserver)

getRecipeByCategory("Vegan").observeForever(responseObserver)

progress.observeForever(loaderObserver)

}

verifyOrder {

loaderObserver.onChanged(true)

loaderObserver.onChanged(false)

responseObserver.onChanged(recipeWithBasicInfo)

}

}

}

Key take away from this test case are,

Use coEvery{} to mock behavior of suspend-able function.

You can also use TestCoroutineDispatcher to use it as a delay controller.

verifyOrder from the Mockk can be used to make sure assertions are happening in an expected chronology.

Note: TestCoroutineDispatcher needs to be reset after every test case is executed. Resetting dispatcher will result into more reliable test cases. Following code can be used to set and reset the dispatchers. runBlockingTest function can also be accessed from TestCoroutineDispatcher instance.

internal class RecipeListViewModelTest {

private val testDispatcher = TestCoroutineDispatcher()

@BeforeEach

fun setup() {

Dispatchers.setMain(testDispatcher)

MockKAnnotations.init(this, relaxUnitFun = true)

viewModel = RecipeListViewModel(repository)

}

@AfterEach

fun cleanUp() {

Dispatchers.resetMain()

testDispatcher.cleanupTestCoroutines()

}

// Your test cases

}

Best practices to follow while writing test cases

When it comes to writing unit tests or any kind of tests, there are few best practices and guidelines which one should follow. Lot of experts has already shared their view points on this. I would re-iterate some of them and I will also share some of the best practices which you should consider while writing your test cases using Kotlin.

Verify one scenario at a time

It is very important what you are verifying within your test case. The code under test could be doing multiple things. But your test should focus on a very specific scenario. For example, getRecipeByCategory is trying to fetch data, handling error and emitting data if it retrieved successfully. But we have added multiple test cases to verify each of these scenarios. We added separate test cases for scenarios when the internet is not available or SomethingWentWrongException is thrown.

You can also limit your assertions. For example, one assertion per test case. I am not a big fan of doing so and I don't recommend this approach. You would end up doing a lot of the same thing in multiple test cases.

Make your test readable

While writing tests the second most important thing to me is to make them readable. If tests are readable that means, it's maintainable. Otherwise, you will get lost in your test when it comes to refactoring the code and tests. The way we used JUnit5 will make them readable when you see the reports, but the code has to readable as well. Jake Wharton has presented the concept of testing robots https://jakewharton.com/testing-robots. Testing robots makes code very much readable. This pattern not just makes the test code readable but also separates the how-to part from what.

Divide and rule

Another thumb rule is keeping your test as small and as isolated as possible. Whatever is your dependency, try to decouple it. See what you can mock. See what you can do with stub data. Provide all required environments which your test needs. For example, we mocked observers and repository while testing our view models. For the view model, we don't care whether API is working or not. We just care what if it works and what if it doesn't. And based on this we mock the situation. Mocking is a guaranteed way of removing the environmental dependency and it will still provide you the reliability and trustworthiness.

Find a good balance between unit and integration test

The nature of the Android espresso test depends on multiple factors. You have to consider a lot of OS version and screen dimensions if you want to be super sure that your application will behave the same irrespective of what OS and hardware configuration the device is using. It is a herculean task to have a stable integration test suit for your application. To tackle this problem, try to balance your test cases by categorizing them. We earlier learned in this chapter how the test can be categorized between small, medium and large. To check whether your login screen validations are working correctly or not, you don't need to run espresso test cases, your unit test should be strong enough to tell you that. For the espresso test, you should only care for high-level user journeys.

Fixed and shared test data

Test data also plays a significant role while writing a test case. Because of wrongly managed test data, you can end up in a situation where even if nothing is wrong, your test cases are failing. They can give you false alarms. Sharing test data is also considered best practice amongst community developers. There are a lot of good blogs written over it. To know more you can read this step by step instruction https://proandroiddev.com/sharing-code-between-local-and-instrumentation-tests-c0b57ebd3200. It explains everything from configuring the source to using it.

Wrapping up

We focused on a very important aspect of software development, i.e. unit testing. Each and every developer should write unit tests. We should not consider writing unit test a separate task. It's is part of your code writing and application development. Whenever you estimate your task, consider effort for writing unit tests as well. Discuss with your QA what are the criteria you should assert. QA will help you to come up with the test cases. This way you will also reduce the burden of your QA friend.

Espresso test cases might be unstable, depending on the type of device you are using. Ideally, you should run your test cases on multiple devices to make sure the application is behaving consistently. Discuss with your technical team and divide your test cases in the category of small, medium, and large.

In the next chapter, we are now targeting to make our application production-ready. Few more important tasks are remaining before we send our application to our end users.

CHAPTER 9

Make Your App Production Ready

"Design with skepticism, and you will achieve resilience."

― Michael T. Nygard

The word "Production Ready" is very subjective. There is no standard definition of it. Each can have various opinions. It has multiple aspects. A software engineer can have a different point of view compared to a product owner. A software engineer might be thinking that zero bugs make application production-ready, but the product owner might be saying that an MVP (Minimum viable product) makes it production-ready. But if your application is having zero bugs but lacks functionality user will not love and vice versa if the application is loaded with features but hardly runs. In both, the case your user w ill curse you and your business will suffer.

Over the period of time industry has found out some very crucial points which cover technical, operational, and usability aspect of any software. This is where the above quotes from Michael T. Nygard make more sense. The reality is… We all plan what to do if we succeed, but we hardly talk about what if we fail. We don't plan for failure. We should doubt each and every assumption we make while developing software. Ask yourself the following questions:

What if my application will crash?

What if someday the server will be down?

What if the user is stuck while using your application?

What if the information shown in the app can have different meanings?

What if someone hacks into your application?

All these questions will help you to design and develop your application better. And this will take you towards making your app production-ready. If you are not thinking about such questions, then you are not developing an application for your users, you are developing an application for your developer and QA only. In the general industry has developed the following criteria which will make your application production-ready:

It should run

It should have all features which help the user to do something

Behaves the same and stable every-time you use it.

Scalable and allows adding new features easily.

Maintainable allows modification and let you know if you break something.

Well documented.

Secure enough to protect your and your user's data.

The traceable system should leave enough trace/logs to debug if something is wrong.

The above list can even grow more if we start capturing tiny details. But according to me, it covers all aspects of Production-ready.

So far we made our application running, it has also got all features which we discussed during Chapter 5: Analyzing and Architecting Meal Recipe App. We also added test cases that make sure our application is maintainable. And test cases are producing the same result every-time so this means the application is stable as well. Apart from these we still have to make some more changes to make our application production-ready.

Structure

This chapter covers the following topics:

Package restructuring

Writing safe code in Kotlin

The static code analysis tool

Documentation

Analytics

Package restructuring

This particular point of package restructuring does not belong here in ideal scenarios. But in the practical world, this always happens. You start with some idea first and later on when your code grows more, you need a better structural representation of your code. So I am deliberately covering this topic here in this chapter. In previous chapters, we focus more on making applications run and stable. But now since the code has grown more and the application is full of features, we need to represent that scale through a solid package structure. The package structure helps you to arrange your class files in a better way. It's a basic shelf for your class files. All class files should live inside an appropriate shelf.

The way each individual can have different opinions and preferences of arranging their items on the shelf, the same way each individual developer can have different opinion of managing the package. I particularly like my android application to structure as per it's feature. I prefer all classes related to it's functionality remains together. For examppple, all classes related to showing a list of meal recipe should live inside, let's say listRecipe or recipeList package. Figure 9.1 shows the structure what we have at the end of Chapter 8: Testing the Kotlin Code. It has some readability issues. To understand this issue better let me give you an example. Imagine a your application is having a bug and user has given a review that, loading recipe is taking a long time to load and after 5 seconds application is crashing and user is not able to use the app after that. And you are a new developer on this code base. What would you do? What was the first step you would do when you start looking into the issue?

You would look for the class which shows list of recipe or shows loader, won't you?

This particular point of package restructuring does not belong here in ideal scenarios. But in practical world this always happens. You start with some idea first and later on when your code grows more, you need a better structural representation of your code. So I am deliberately covering this topic here in this chapter. In a previous chapters we focus more on making application run and stable. But now since the code has grown more and application is full of feature, we need to represent that scale through a solid package structure. Package structure helps you to arrange your class files in a better way. The way each individual can have different opinion and preferences of arranging their items in shelf, same way each individual developer can have different opinion of managing the package. I particularly like my android application to structure as per it:

Figure 9.1

Figure 9.2

Compare the difference between Figure 9.1 and Figure 9.2. The latter screams more about its feature and hence more helpful to understand where the specific file could be. To achieve this kind of a package structure, you need to create the packages for your high-level application feature. And after that create new classes according to that or move your existing classes.

Tip: Drag and drop file to any desired package and its references will automatically get updated throughout the project. The Android Studio will also update its references in the Manifest file. It will not upgrade it's test class files, that you have to do it manually.

Writing safe code in Kotlin

Kotlin is a programming language. It's not any security protocol or framework. So we cannot expect here some core security principles. We will not talk about encryption-decryption techniques here. Since it's a JVM language, so it does not offer any more core security features on top of Java. By security I mean safety. Kotlin as a language as so many features and tricks that can save you from disasters in the production environment. For example null safety. This single feature can save you from a lot of application crash which you can't even think of. Lint is another powerful tool that you can use to catch errors. It also helps you to keep your all files with the same alignment and code formatting styles. And the most important thing is you can also automate your lint checks, so you can make sure all code merge request follows the same standard. The static code analysis is dedicatedly covered in the next section.

Use val over var

Such a basic feature of Kotlin. val takes you towards immutability. And immutability guarantees you that once the value is assigned it cannot be changed ever again. So there remains no scope of the value being changed from any other function, thread, or operations. On the contrary, you have to explicitly declare the variable with var if you want that value to be changed later on by any function, thread, or operation.

All function parameters are by default marked as val. And you cannot change the value of those functions within that scope. The following code will give you a better idea about what I am trying to say:

val doubleTheAge = Person("Hardik",32).let{

it.age=64 // Throws compile time error

it.age*2 // Ok age is a read only

}

Use data class as POJO

Whenever you want to just hold the data which used for some comparison purposes, use data class over normal class. We all learned in initial chapters that data class gives you equals() and hashCode() for free. In most cases, you don't need to worry about the object being different and you can blindly trust this.

The only caveat in the data class comparison is the equals() and hashCode() contract only works properly if vales are assigned using constructor only. If there is any property that is not part of the constructor data class can give you a weird result. Check out the following code:

fun main() {

val p1 = Person("Hardik", 32)

val p2 = Person("Hardik", 32).apply { address = "London" }

val p3 = Person("Hardik", 32)

println("====== Structural equality =====")

println(p1 == p2)

println(p1 == p3)

println("====== Reference equality =====")

println(p1 === p2)

println(p1 === p3)

}

data class Person(val name: String, val age: Int) {

var address: String = ""

}

//Output

====== Structural equality =====

true

true

====== Reference equality =====

false

false

Change the value of age, for instance, p1 and see what the output is. You would be amazed to see how the property address is not even considered while comparing the object.

Return immutable type

Always keep immutability in mind, we already now know that we should prefer val over var. But we should also consider immutability while dealing with types. Never return mutable types from function. Because you will have a risk that caller class or function can modify the data which is returned from the function and it can have a negative impact on the code. Let's pretend you have created following view model class:

class RecipeViewModel {

private val recipe = mutableListOf<String>()

fun getRecipe(): MutableList<String> {

(1..3).forEach {

recipe.add("Recipe $it")

}

return recipe

}

}

The class has a private list of recipes and a function which returns the list of the recipe which ultimately contains 3 recipe item.

With the above code your caller class is capable of doing following changes:

fun main() {

val recipeViewModel = RecipeViewModel()

val recipes = recipeViewModel.getRecipe()

println(recipes)

recipes.add("Hazard :D :D")

println(recipes)

}

// Output

Before modification the state of recipe instance which is private

[Recipe 1, Recipe 2, Recipe 3]

after modification the state of recipe instance which is private

[Recipe 1, Recipe 2, Recipe 3, Hazard :D :D]

Do you see the severity, even if your instance is private your public method is exposing it as mutable instance and anything can be done with your private instance? This is not safe, hence the function getRecipe should return List<String> instead of MutableList<String>. If you return the interface type no one can modify it outside its scope, and the compiler will throw an error if someone tries to do that.

The static code analysis tool

An analysis done by software without running the code is called static code analysis. Here your code file is read by a software and certain checks are performed to ensure the code meets industry standards. The main advantage of doing so is to catch the error in the early stage. You can potentially catch a lot of errors before your code goes live. There is a lot of static code analysis tool available for Java. But what about Kotlin. In the last three years, Kotlin has grown so much. Now there are good enough tools available that support Kotlin as well.

Advantages of static code analysis tool

Below are some advantages of using a static code analysis tool for your application development.

Early and frequent checks

With such tool you get to know possible errors and improvement during development stage itself. It saves lot of time while you sent your code for reviews. The reviewer can focus on much important details like approach, business logic. Reviewers don't need to worry about tiny improvements like removing unused imports or variables. Such tools also allows you to configure your own rules, for example, tab spacing, line break. Also you can configure the error indicator. For example, you can force not to build the application if there is any unused class in the source code. And code analysis tool will obey that.

Increases robustness of your code.

Nowadays code analysis tool are not just for fixing alignment and removing unused imports. They actually suggest you optimisation and even improvements in logic. For example, improving loops, indicating usage of val over var, suggesting possible usage of lambdas, better syntax, etc. And all this improvements contributes towards the robustness of your application. The code become safer, performant, and secure.

Reports and modelling diagrams

The new tools are so smart that they can generate lots of fancy reports which can help developers and managers, to understand the state and quality of code. They can also now generate important diagrams like, class diagram, call graphs, dependency graphs, etc.

detekt vs ktlint

For Kotlin there are two main contender competing to get more market share in the industry. They are respectively detekt and ktlint. Let's see what their official definition says:

ktlint: An anti-bike shedding Kotlin linter with built-in formatter.

detekt: a static code analysis tool for the Kotlin programming language. It operates on the abstract syntax tree provided by the Kotlin compiler.

Ktlint highlight itself as a linter and detekt highlight itself as static code analysis tool. So what is the difference between linter and static code analysis tool? Well linter only focus on styling, code formatting and memory leaks. While static code analysis tool focus on all lint issues plus finding possible bugs, cyclomatic complexity, code smell issues, etc.

I have focused completely on detekt here. It's loads of feature and much more than just linter. It provides very easy integration with IntelliJ IDEA and Android Studio. There is a plugin, available on the marketplace of these IDEs. It's just one click away. A must have plugin for each and every developer. You can install it from market place as depicted in Figure 9.3 and Figure 9.4 shows to enable it. If you don't enable, it won't work:

Figure 9.3

Figure 9.4

Once the detekt is enabled, you will start seeing warnings in some of your class. The most common warning I see in the meal recipe app is shown in Figure 9.5:

Figure 9.5

You don't need to panic when you see warning all over your project. You should be happy because you will be doing this many improvements. The warning in Figure 9.5 is basically about missing a newline (\n) character at the end of file. Just press enter at the end of file and warning will be gone. The problem with the IDE level plugin is you cannot generate report for your entire project. You need to visit each file, to know, and fix the errors.

Tip: Use gradle plugin to get more customisation and report generation ability. If detekt is configured inside your build pipeline, you can automate it and make sure for each merge request you run this task and make sure the quality of code remains non-negotiable. Follow steps here to configure detekt as your gradle plugin https://arturbosch.github.io/detekt/kotlindsl.html

It allows you to generate reports in various formats e.g. xml, html, txt. Default format style is .html

Though detekt IDE level plugin also allows you to modify and customize the rules as per your need. Once the plugin is set up, it generates default-detekt-config.yml file. This file holds all set of rules. And comes with tons of default configuration to give you a quick start. The .yml file can be found in the root of your project directory.

Fine tune default config

The default config which the plugin will generate, needs, some fine-tuning. There are lot of crucial check which are marked disabled. My personal preference is to keep them enabled. Below are some configuration which we tweaked to make our code more robust and safe.

Enabling code commenting

I like to enforce developer team to write more and more documentation. For each public class, property and function I highly recommend some sort of documentation. Apart from naming them appropriately it is also important to provide enough documentation explaining purpose of the function. Inside comment config, you can mark below configurations as true:

UndocumentedPublicClass:

active: true

searchInNestedClass: true

searchInInnerClass: true

searchInInnerObject: true

searchInInnerInterface: true

UndocumentedPublicFunction:

active: true

UndocumentedPublicProperty:

active: true

Exception handling

Lot of developer has a habit of swallowing the exception, or they end up throwing too generic exception. This does not help. I always prefer to do something extra after the exception is caught. I also prefer not throwing generic exceptions like Exception, Error, Throwable, etc. For that checkout the following code:

SwallowedException:

active: true ignoredExceptionTypes: 'InterruptedException,NumberFormatException,ParseException,MalformedURLException'

allowedExceptionNameRegex: "^(_|(ignore|expected).*)"

TooGenericExceptionThrown:

active: true

exceptionNames:

- Error

- Exception

- Throwable

- RuntimeException

Some small yet significant improvements

The default config is mostly good enough, we still end up doing above two major changes in that. Similarly, there are a lot of other small improvements you can do for your project. But the choice of keeping it enabled or disabled is depends on individuals' preference. I would like to put some restriction on the below configurations:

Class naming: Avoid using class names like MyApplication, MyAdapter. You can add regex for this and detekt will start showing such errors.

Number of functions in a single class: You should limit the number of functions to be used in a single class.

Number of parameters in function: Having too much argument in the function name parameter is considered bad practice. For such cases, you should consider using some class to encapsulate all this parameter into one.

Indentation and number of lines in class: Limit the tab spacing and the number of lines you can have in your class. If the class is too long consider dividing it by either using principal of composition or inheritance, based on your requirements.

Changes we did as per detekt recommendation

We enabled detekt and followed it's a warning. And we could do the following improvements in the code.

Removing wildcard imports

To avoid naming conflicts with local namespaces, using imports with a wildcard (*) is not considered a best practice. We could remove all such imports with the help of detekt:

// Before

import java.util.*

//After

import java.util.Locale

Adding missing documentation

Enforced rule of adding documentation for all public classes, interfaces, properties, and functions helps a lot to find out all places where documentation needs to be added. This can be a very significant improvement if you are writing an SDK or library. Your consumers will get well-documented APIs. In the further section, we are going to generate the complete documentation of our project. Adding documentation for all classes, interfaces, properties, and functions will help us a lot there.

Parameter list wrapping

When we use parameters for any function, keeping them readable and aligned is important. Many times we end up having them in weird formatting. We did not focus on a line break. Kotlin recommends write each parameter in a new line if it does not fit in a single line:

// Before, parameters do not fit in a same line

override fun onCreateView(

inflater: LayoutInflater, container: ViewGroup?,

savedInstanceState: Bundle?

): View?

//After, each parameter with \n in new line

override fun onCreateView(

inflater: LayoutInflater,

container: ViewGroup?,

savedInstanceState: Bundle?

): View?

Dokka as documentation engine

Detekt made us work a lot on the documentation. We wrote documentation above each and every classes, interfaces, properties, and functions which is publicly visible. Why we did that, for our developer team? Yes! But also we wanted to release this documentation in a readable format. This readable format can be HTML. This HTML report will make more sense to any non-technical team member, for example, Business analyst, Quality analysts, Product owner, etc.

Dokka is a documentation engine written and maintained by Kotlin's team only. It produces the same style of documentation as JavaDoc. But this engine is meant to understand Kotlin more. This does not mean that it cannot understand Java-style code comments. It fully supports both Java and Kotlin style documentation. It can generate documentation in multiple format i.e. HTML, markdown file, and JavaDoc. It defaults to HTML though.

Note: From version 0.10.0 dokka supports also supports KMP (Kotlin multi-platform). If you want to use dokka for multiplatform you need to change the way you configure dokka. Inside the build.gradle the configuration block needs to be replaced with multiplatform. The more FAQs are documented here https://github.com/Kotlin/dokka/wiki/faq

A successful plugin will generate a dokka Gradle task. You can run this task to generate the documentation. Following is the command to generate the documentation for your project:

./gradlew dokka

The tasks take a couple of minutes to finish. The time taken by the task can depend on the size of your application and the amount of documentation you have added.

Dokka report

A report is generated by default inside your build directory. If the task is completed, you should find the report inside /build/dokka. Refer following Figure 9.6:

Figure 9.6

If you open this index.html file in the browser, you can see nice documentation being generated. You can copy this task to some remote location and can be accessed with the URL by anyone.

Tip: If you are developing an SDK or library, run dokka task during each application release. And share the documentation along with the changelog with your developer team. They will love this.

Analytics using Firebase

Analytics can give you much more insight into your user's and application's behavior. There is plenty of analytics available in the market, Firebase, Mixpanel, Adobe, App Center to name a few reputed analytics tools available from the giant like Google, Microsoft, and Adobe. They have their pros and cons and you can choose any of them based on your requirements. For this application, we have decided to go ahead with Firebase.

You need to create an analytics project on the web portal first. Follow the step by step process here https://firebase.google.com/docs/android/setup

Firebase offers free unlimited analytics which includes

A/B testing

Application crash logs

User segmentation

User behavior prediction

Unlimited event reporting

We are not covering analytics as a whole chapter here. There is a lot of things to discuss in analytics. It can go very complex when we start talking about tracking custom events or funneling them.

We are covering this topic very briefly just to prove a point that the application cannot be considered production-ready, if it does not give insight. All data which will be generated by analytics can give a lot of insight and help your product grow more.

After running application on a few of the devices, below Figure 9.7 is the report which firebase has generated:

Figure 9.7

Wrapping up

After all the hard work, we are now at the stage, where the application can be released. From the perspective of application publisher, you still have to perform a lot of steps that can be found here https://developer.android.com/studio/publish.

But from the developer's perspective, the application is now ready to be released. We have written a good quality code, added tests, considered best practices, added monitoring systems like analytics and crash logger, wrote nice documentation. We developed an app by writing an entire application in Kotlin, we also used some third party libraries and framework which are fully written in Kotlin. The entire tech stack has focused on Kotlin completely.

In the next chapter, we will learn about Kotlin's capability outside the Android world.

CHAPTER 10

Kotlin Everywhere

How many times you have come across a programming language which can do almost everything? At least I haven't come across. I started my career in the world of web. I learned J2EE and worked on a couple of web portal development. As and when I started learning Java and I realized, Java is able to perform pretty much everything. I knew there is J2ME which is used to develop Java-based mobile applications. In 2008-2009 there was a boom in Java-based mobile phones, everyone was behind them. Nokia N series was a status symbol. Sony and Motorola also joined the party. When all this was happening, Apple has already launched it's first-ever iPhone in 2007. I was in India and there iPhone was yet to arrive. But I started reading about it in my university library's journal. And I found out they were not using Java for developing those phones. It was a complete new world of fancy and robust Mac machines and a programming language called objective C. I wonder what is the objective of using Objective C? Why Java was not enough. I started reading about other such languages and came across languages like C#, PHP, etc. I found out all languages are used to achieve different objectives. I again wondered, why one language cannot be used to achieve everything. I was still doing my masters and it was my final year in university. I was buckling up with Android those days. A competitor of iOS. Android was using Java and some of the things were highly influenced by the Swing framework. Oh god! So many things to learn and catch up with. I wish there was a programming language that can be a one-shot answer to the web, mobile, desktop and scripting programs and applications.

Well, I was not exceptionally talented, but someone in JetBrains was. In 2011 when iOS and Android were gaining momentum amongst smartphone buyers and developer communities, JetBrains announced a new project named Kotlin. At that time Kotlin was targeting to have new features that programming languages like Java and Scala were not having. Kotlin was named after an island in Russia, near St. Petersburg. Kotlin was not aiming to solve all problems with one programming language. They just wanted to have a cool looking programming languages packed with features which they wanted. The project Kotlin was taking shape and growing but within the cocoon of JetBrains only.

Now comes the year 2016 where they announced it's first-ever stable release Kotlin 1.0. They promised long term backward compatibility and 100% interoperability. Java 8 was new at that time. All the software including Android was using either Java 6 or Java 7. The syntax of Kotlin suddenly caught the attention of the developer community. The learning curve was small for Java application developers. Kotlin was taking away all the verbosity and boringness of Java. Syntax was so fresh and attractive. From this first release, they also announced it's support for multiple platforms, For example, Server and JavaScript. After Google officially announced the first-class support to Kotlin, the developer community welcomed it with a very open heart and Kotlin. Kotlin has lived up to everyone's expectations. Kotlin is now easily adopted amongst the developer community.

Structure

This chapter covers the following topics:

Kotlin ecosystem

How Kotlin can be used for platforms like iOS, web, embedded systems, desktop, etc.

How to share code between Android and iOS using Kotlin multiplatform

Cool and useful Kotlin open-source projects and libraries

Kotlin ecosystem

JetBrains has released some interesting numbers on its website https://www.jetbrains.com/lp/devecosystem-2019/kotlin/. This explains how Kotlin has grown and adopted at the end of the year 2019. Some of the numbers were surprising to me. For example, as per Figure 10.1, 66% of Android developers have moved from using Java to Kotlin for their Android application development. I was expecting more numbers here, but I was shocked to see when JetBrains said 57% of the developer community targets JVM for it. It's more than 50%. This shows how Kotlin is welcomed and enjoyed running it on production. I have been working on Kotlin for more than 3 years now. I haven't seen a single issue on production which is caused by Kotlin. Kotlin is so much reliable:

Figure 10.1

Apart from this below are some more interesting facts and figures:

Till 2019 Kotlin is most popular and easy-going with JDK 8.

Amongst Android developers 79% of targets Kotlin for Android 8 (Oreo) followed by Android 7 (Nougat) 75% and Android 9 (Pie) with 74%.

Kotlin's adoption is less in the world of Browser side JavaScript and Server-side Node.js. But between them, JavaScript has shown more adoption.

Mobile apps are being developed most using Kotlin with 62% of developers. Web backend development is finishing 2nd in a race with a percentage of 41%.

Yet another interesting fact is out of all the Kotlin developers 71% of them use Kotlin for their work projects that mean some serious production level work.

Amongst all Kotlin developers, their secondary language preference is Java (86%), JavaScript (61%).

Kotlin – Jack of all trades

Take a look at the following Table 10.1 which shows the capability of Kotlin to achieve various things. You can achieve everything. There are a lot of other possibilities which I haven't covered here. For example, writing scripts is possible with Kotlin. You can share the code between platforms. We will cover this in detail in the next section:

Table 10.1

Python is certainly one of the most popular languages when it comes to introductory programming languages. Python is an open-source as well. But when it comes to embedded system and devices which runs such type of software, python would be never the first choice. Rather developer will choose C or C++, because of its immense capability to deal with the system. And I think C/C++ would still dominate as embedded system programming language compares to any other language. With python, there are certain flavors which claim to be able to communicate with low-level systems like microcontroller. MicroPython (https://micropython.org/) is an efficient solution to this. They have extracted some of the core functionality from Python3 and made it possible to run on microcontrollers.

On the other hand, C/C++ can do anything when it comes to developing software. The simple reason is its potential to communicate with low-level systems. But C/C++ would not be the first choice for standalone systems because there are a lot better languages, tools and frameworks that are designed to do this job. For example, Java, C#, etc. Also, in some cases, you will always need C/C++ source code to run the code on any machine. It just can be deployed and run on any machine on any other day. And for JavaScript, we have to accept that C/C++ just can't cut the mustard.

For C/C++ both Android and iOS have given some out of the box support. The reason is again the same, it's the capacity to communicate with the low-level system. There is native support for C/C++ on both mobile platforms. For Android, this support is given under the name of NDK (Native Development Kit). The Android studio also supports this. You may need to download from extra things through your SDK Manager.

I used to love C# a lot. Until I started working on Java. Java has a really wide set of applications and developer communities. C# also has a large community. And C# has mastered some of the programming concepts and it has its community base who are just in love with this. But as you notice in the above table they never grow in the area of JavaScript, Mobile platforms like Android and iOS and embedded systems.

In connection with Kotlin, from day one, they aimed to support multiple platforms. Currently, they support all the platforms from the above table:

Kotlin supports standalone applications using TornadoFX. TornadoFX is a framework to build a JavaFX application using Kotlin.

Server-side application development is supported because you can write a spring boot application in Kotlin. You can transpile (Translate and Compile) Kotlin code into Node.js easily. It doesn't require you to be Node.js expert to achieve this.

Just like Node.js, you can transpile Kotlin into JavaScript. Node.js is also a JavaScript framework but runs on a server-side. So, it's possible to generate JavaScript code and run on a browser. By doing this you may not need to worry about supporting various browsers because Kotlin will generate that code for you.

What do I say about Kotlin's support for Android? It's already proven. Google has already announced its support for Kotlin. Millions of apps are already using Kotlin and they are in production.

If you are an Android application developer and want to develop and iOS application, and you are stuck because you don't have enough knowledge about Swift. Kotlin can be a great help. Because Kotlin now supports iOS application development. Kotlin can work with the cocoa touch framework very easily. Also, you can write a shared module between the Android and iOS platforms and using swift code you can call this shared Kotlin module. Check out the sample straight from the maker IntelliJ IDEA https://github.com/JetBrains/kotlinconf-app. Kotlin has announced this support under the name of Kotlin native.

When Kotlin is not leaving any platform behind, why they would leave embedded system domain unexplored. Therefore, Kotlin's team has provided excellent support for the embedded system under the same Kotlin native technology. It has capability run without the virtual machine and that's how it's just perfect for microcontrollers.

A couple of years ago, in my book Kotlin blueprints https://tinyurl.com/y4msoc2p which I co-authored, was covering all the platforms with fairly great details. There was a dedicated chapter on all these technologies and platforms. It also had a sample project so that each concept can be covered in depth. I would highly recommend that book in the case in-depth understanding is required on any of the above platforms.

Other than Android, we will touch upon each platform and understand how Kotlin works on each of these platforms:

Figure 10.2

The above Figure 10.2 explains which platform Kotlin is targeting. So how Kotlin does it? From the above table, you might have got a blur idea about it. Following Figure 10.3 is one more attempt from my side to make things clear for you:

Figure 10.3

Kotlin for the standalone app

When it comes to developing stand-alone applications, which run on desktop machines, there are many possible ways. Microsoft has a fantastic framework available which is .Net. Powered by .Net framework and Visual Studio you can develop amazing and modern desktop applications. You use C# language to program using these tools. The entire concept comes under WPF (Windows Presentation Foundation). It is a framework that helps to create a desktop-based application. Kotlin cannot be used in conjunction with Visual Studio and .Net framework. Since it's a JVM language it needs JVM to run and not the .Net framework. If we want to use Kotlin we need to start seeing JavaFX as the option.

JavaFX is a Java-based solution to develop rich stand-alone applications. This framework is having a set of libraries which will help to create and render UI element and allows you to manage media e.g. images, audio or video, etc. JavaFX is already a proven technology when you talk about standalone applications. To highlight some of the capability, the below points can be presented:

FXML and CSS like styling: It is a mark-up language where you can declare your UI element. Along with this it also supports CSS like styling. So, you can build a rich interface with just a little knowledge of CSS and separate your UI and styling logic from your application logic.

Build-in UI controls: Has a set of all basic UI elements which can be required to develop desktop applications.

Multitouch support: It supports multitouch if the OS and hardware are supportive.

Media engine: A great support has been provided to process the media. You can manage a variety of content and render high-quality graphics.

Swing interoperability: Previously Java applications used to be developed using Swing and JavaFX provides very good interoperability with Swing application. This makes adoption to the JavaFX framework very easy and smooth.

In-build application deployment model: It has all required packages and resources which will help to deploy the application in JavaFX runtime.

There are few more features where support is provided by WebView and Canvas API. JavaFX as a package is very easy to deal with. But we are not going to use JavaFX for this. There one more framework available where the excellent Kotlin support is provided. The framework is TornadoFX.

Why TornadoFX

TornadoFX claims below features on https://github.com/edvin/tornadofx:

Supports both MVC, MVP, and their derivatives

Dependency injection

Typesafe GUI builders

Typesafe CSS builders

First-class FXML support

Async task execution

EventBus with thread targeting

Hot reload of Views and Stylesheets

OSGi support

REST client with automatic JSON conversion

Zero configs, no XML, no annotations

You should check out some of the examples which explain how TornadoFX can be used to create awesome standalone applications https://edvin.gitbooks.io/tornadofx-guide/

Kotlin for server-side development

Kotlin's team is also targeting server-side development. You can easily perform the following:

Develop web applications using servlets

Develop REST APIs using Spring Boot

Develop back end using Node.JS

For this section, we will only focus on Servlets and Spring Boot. We will cover Node.JS into the next section where we will focus on JavaScript and frameworks like Node.JS.

Two years ago, I did an in-depth study of using Kotlin for server-side programming. I developed a multi-user chat application. The code was entirely written in Kotlin. The code was ultimately transpiled into Node.js compatible code. The example code can be found here https://github.com/hardik-trivedi/Kotlin-Blueprints/tree/master/Chapter05. For this, the HTML was also generated using Kotlin code. I used Kotlin DSL for that.

The project does following tasks,

Renders HTML pages using EJS.

Uses Express.JS as its framework, which makes everything easier to deal with Node.js.

The entire code is written in Kotlin, compiled into the Node.js code.

It uses Socket.IO to send packets containing chat information.

To get some more insight into how everything made possible, have a look at the following code. I have marked comments in bold which explains everything. The code is not capable to work all alone but good enough for an understanding purpose:

fun main{

// Set up required dependencies

val express = require("express")

val app = express()

val path = require("path")

val bodyParser = require("body-parser")

val http = require("http")

// Get port from environment and store in Express.

val port = normalizePort(process.env.PORT)

app.use(bodyParser.json())

app.set("port", port)

// Configure router and create a server

val server = http.createServer(app)

app.use("/", router())

// Initializing socket.io, a chat framework

val io = require("socket.io").listen(app.listen(port, {

println("Chat app listening on port http://localhost:$port")

}))

/**

* There will be event like sending messages, receiving messages. A

* message can be send with information of sender and actual message.

*/

io.on("connection", { socket ->

socket.on("new_message", { data ->

val userJoinedData = json(Pair("nickName", socket.nickname), Pair("message", data))

socket.broadcast.emit("new_message", userJoinedData)

})

})

}

Do I need to explicitly draw attention one more time that, the whole code is written in Kotlin. Don't just believe it, give it a go and try on your own.

Kotlin for JavaScript

Since the above section is focused on Node.js, there is also a dedicated application that is created to demonstrate how Kotlin can be an alternative to JavaScript. When you replace Kotlin with JavaScript, the biggest advantage is type safety. Also, you get the benefit of statically typed languages. You get to know all errors in advance. There is no different opinion that JavaScript error messages are so cryptic that even experienced developers find it difficult to understand. You will be mostly clueless during debugging and you have to stress your mind a lot.

This example https://github.com/PacktPublishing/Kotlin-Blueprints/tree/master/Chapter04 can explain to you how to use Kotlin which generates HTML code and performs validation and API call using JavaScript. A simple weather application is developed to demonstrate this.

Just like the above section, let's see how Kotlin can work as JavaScript code:

// A simple div which will act as container.

<div id="container" class="weatherContainer">

</div>

// Example of an API call on click of a buttin and parsing it's JSON response.

button(classes = "getWeatherButton") {

+"Get Weather"

type = ButtonType.button

onClickFunction = {

val zipCode = document.getElementById("zipCode") as HTMLInputElement

val xmlHttpRequest = XMLHttpRequest()

xmlHttpRequest.open("GET", FULL_URL + zipCode.value, false)

xmlHttpRequest.send()

val forecastResult = JSON.parse<ForecastResult>(xmlHttpRequest.responseText)

showData(forecastResult)

}

}

/**Iterating over response and generating HTML using Kotlin DSL.

* A runtime HTML is created and each element is appended in main

* container.

*/

fun showForecast(forecastResult: ForecastResult) {

val weatherContainer = document.create.div()

forecastResult.list.forEachIndexed { index, forecast ->

with(forecast)

{

weatherContainer.appendChild(document.create.div(classes = "weatherBlock") {

div {

p(classes = "primaryLightText") {

+dt.getFullWeekDay()

}

p(classes = "secondaryLightText") {

+dt.getShortDate()

}

p(classes = "currentTemp") {

+"${Math.round(temp.day)} °C"

}

}

div {

with(temp) {

span(classes = "tipText") { +"max " }

span(classes = "primaryText") { +"${Math.round(max)} °C" }

span(classes = "secondaryText") { +" /${Math.round(min)} °C" }

span(classes = "tipText") { +" min" }

}

}

onClickFunction = {

showDetailedForecast(forecast = forecast, city = forecastResult.city)

}

})

}

}

document.getElementById("container")?.appendChild(weatherContainer)

}

Note: The above example is just to give you a glimpse of Kotlin's capability. The real-time application can be complex. And there were be a lot of challenges you will face while working on such kind of tech stack for the first time. The only problem I see here is there is not enough documentation provided from Kotlin's team. So as a developer sometimes you struggle to get the most out of Kotlin when it comes to browser and server-side programming.

Kotlin for iOS using Kotlin multiplatform

A very fundamental trait of good software and software engineers is reusability. One should always keep this in mind. The same principle stands true when it comes to code sharing. I feel the future is going towards sharing the code between platforms. There are already good frameworks that do the same. Of course, reusability and code sharing is achieved in various ways by several frameworks. For example, React native uses JavaScript to generate native code for Android and iOS. Cordova uses HTML, CSS, and TypeScript and runs the code in the web component so that both Android and iOS can render the content.

Kotlin does this in a different way. The approach which Kotlin has taken is very much scalable. In this section, we will learn how we can share the code between Android and iOS. Some of the common logic can be shared. This will be mostly business logic, which is platform agnostic. There are ways defined by Kotlin to perform platform-specific logic if required.

Note: When I tried using Kotlin multiplatform to share code between Android and iOS, everything worked like charm. But this is still in the experimental stage. This means I cannot recommend it to use it for production.

In this section, we will learn how Kotlin code can be shared between Android and iOS. The problem statement we have selected to check Kotlin's code sharing capability is the currency converter. We want to convert from Pound sterling GBP to Indian rupees INR. Along with converted amounts, we will also display conversion rates and conversion fees. Conversion fees would be different for Android and iOS. Take a look at Figure 10.4 which will give your idea how the user interface would be looking.

What part of the code will be shared across Android and iOS?

We will be only sharing core logic and not the complete application. We would share the following piece of code and logic:

Currency rate.

Define a contract where both Android and iOS have to provide the implementation. This will allow to differ Android and iOS. We may have such requirements while developing a real-world application. The entire logic would be still written in Kotlin.

Key business logic where currency conversion will take place.

Figure 10.4

The ultimate goal is to create a framework using Kotlin. This framework is imported and used in Xcode which will serve as a library. Since it would be Xcode compatible framework, for Xcode it's just a dependency. Xcode will not even know that the framework is written in Kotlin. For Xcode, it's just another framework.

What tools do we need?

To accomplish this task, we need Android Studio3.4 and above and Xcode 10 and above.

We have to use Kotlin 1.3.50 and above.

Android Studio is needed to perform Android related task and also the shared code will be written using Android Studio. Also, Android Studio is a very handy tool to deal with Gradle. Gradle will play a very crucial role here as using Gradle only we will be generating all iOS required files.

Xcode is needed to create UI and writing remaining logic using swift to bridge the gap between shared framework and actual iOS app.

Let's try to build this in a step by step approach.

Create an Android application

Create a simple Android application with Empty Activity using Android Studio wizard. I chose com.hardiktrivedi.kmpandroidios as a package name and KMPAndroidiOS as an application name. You can give any name of your choice. Create a layout similar to Figure 10.4. The following extract from the layout I have created will give you some idea, specifically around the IDs I am using for each view element. And I have removed the unnecessary noise from the file:

<?xml version="1.0" encoding="utf-8"?>

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http://schemas.android.com/apk/res-auto">

<TextView

android:id="@+id/welcomeTextVIew"

style="@style/TextAppearance.AppCompat.Display3"

android:text="@string/currency_converter" />

<EditText

android:id="@+id/fromCurrencyEditText"

style="@style/TextAppearance.AppCompat.Large"

android:inputType="numberDecimal" />

<TextView

android:id="@+id/fromCurrencyNameTextView"

style="@style/TextAppearance.AppCompat.Large"

android:text="@string/gbp" />

<TextView

android:id="@+id/toTextView"

style="@style/TextAppearance.AppCompat.Display1"

android:text="@string/to" />

<TextView

android:id="@+id/toCurrencyTextView"

style="@style/TextAppearance.AppCompat.Large"

tools:text="INR"/>

<TextView

android:id="@+id/conversionFeeTextView"

style="@style/TextAppearance.AppCompat.Large"

tools:text="Fee" />

</androidx.constraintlayout.widget.ConstraintLayout>

Now we will create the shared module.

Creating a shared module

The core business logic will live inside the shared module. To create a shared module, just create a directory inside the root directory of project KMPAndroidiOS. Name that directory as SharedCode. You can choose any other name as per your convention. Inside the SharedCode apart from core logic, we will also add Android and iOS specific implementation which core logic can require. For example, if core logic needs different currency conversion fee for Android and iOS that needs to be defined within the scope of SharedCode module. A Gradle file inside SharedCode needs to be created something like this https://github.com/kotlin-hands-on/mpp-ios-android/blob/master/SharedCode/build.gradle.kts

This file has a setup instruction for the compiler. This will give the idea to the compiler that where the code will live for common, Android and iOS logic. It indicates where the sources will be. Once you create the build.gradle.kts file, you need to trigger Gradle sync. It will download and configure all required libs. Make sure you have specified the module name inside settings.gradle. This file will be in root directory of your project. Our settings.gradle looks like the following:

include ':app',':SharedCode'

rootProject.name='KMPAndroidiOS'

Writing common implementation

Now we need to write the common logic for the currency converter. Create commonMain directory. Inside commonMain create Kotlin directory.

Once these directories are create, create a Kotlin file, you can name it common.kt. Following is an example from our application:

package com.hardiktrivedi.kmpandroidios

import kotlin.random.Random

expect fun getServiceFee(): Int

expect fun getPlatformName(): String

val conversionRate = Random.nextDouble(90.0, 97.0)

fun convertToGBP(fromAmount: Float): Double {

return fromAmount * conversionRate

}

Let's spend some time understanding what the above code is doing. The functions which are marked as expect keyword is a sort of abstract functions. They cannot have a body, you will get a compilation error if you try to do so. These functions which are marked as expect keyword will be implemented inside platform-specific folders. conversionRate is a property that will get some random value generated every time. And convertToGBP is a core function that will do the actual currency conversion. You may see a compilation error like Figure 10.5. This is because Kotlin compiler wants to make sure that you have provided implementation for the expect declaration:

Figure 10.5

Writing Android implementation

Once you are finished with writing common code, it's time to provide implementation to platform-specific logic. Create a file actual.kt (you can use any desired name). The file needs to be created such that it can have the following path, i.e. SharedCode/androidMain/kotlin/actual.kt. The root directory name containing Android logic has to match with the one which you have specified in your source set inside build.gradle.kts of SharedCode. Inside actual.kt for androidMain you need to write implementation for the function getServiceFee() and getPlatformName(). In our project actual.kt inside androidMain looks like the following:

package com.hardiktrivedi.kmpandroidios

actual fun getServiceFee(): Int {

return (1..5).shuffled().first()

}

actual fun getPlatformName() = "Android"

As a service fee again we are mimicking the changes in the service fee. For Android users, we want to charge service in between 1 and 5 GBP. This demonstrates how you can target platform-specific implementation and yet those values can be used inside your core common logic.

Writing iOS implementation

Now is the time to write iOS-specific implementation. Similar to Android create a file actual.kt at SharedCode/iOSMain/kotlin/actual.kt location. The best practice is to keep the file name exactly the same, for androidMain and iOSMain. The code for iOS-specific implementation can look like the following:

package com.hardiktrivedi.kmpandroidios

import platform.UIKit.UIDevice

actual fun getServiceFee(): Int {

return (6..10).shuffled().first()

}

actual fun getPlatformName(): String = UIDevice.currentDevice.systemName()

By the way, did you notice how Kotlin is also imitating iOS APIs? UIDevice is a class that comes as a part of iOS developer SDK.

Integrating shared code in Android

Android integration is going to be very simple. Just sync the project with Gradle files. You can force Gradle to perform sync from File | Sync Project with Gradle files option. If the result is successful, that means you are ready to integrate. As a first step now add a reference to SharedCode inside your app level build.gradle files. Add following line inside that file:

dependencies {

implementation fileTree(dir: 'libs', include: ['*.jar'])

implementation project(':SharedCode')

// Other gradle dependencies

}

We have already developed a user interface, now we just need to add a logic where required functions are called from SharedCode. For example, when the user enters the amount in EditText, it should trigger the function call and perform the currency conversion. I have created a simple function inside the Activity which is shown below:

private fun performCurrencyConversion() {

toCurrencyTextView.text =

"${convertToGBP(fromCurrencyEditText.text.toString().toFloat())} INR"

conversionFeeTextView.text =

"Conversion rate 1 GBP = $conversionRate INR\nConversion fee for ${getPlatformName()} is ${getServiceFee()}"

}

And the function performCurrencyConversion can be called on the text change event of the EditText which has fromCurrencyEditText as ID. Following the code which does the same:

fromCurrencyEditText.addTextChangedListener(object : TextWatcher {

override fun afterTextChanged(s: Editable?) { }

override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { }

override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {

performCurrencyConversion()

}

})

Note: This approach can be easily adopted irrespective of what architecture pattern you are using. For example, performCurrencyConversion can be called from either Presenter or ViewModel, or the Usecase class.

At this point, you should sync your Gradle files one more time and you are good to go if the sync doesn't fail. Your application should build now and the app should be running.

Integrating shared code with iOS

The time has come, for those who were waiting to see Kotlin code working in iOS. In this section, we will learn how to create a framework that later on going to use as a dependency for iOS applications.

Generating iOS framework from SharedCode module

You will need Android Studio to perform this step. Inside your Android Studio you need to run the Gradle task, as shown in Figure 10.6:

Figure 10.6

You can also run a Gradle task from the terminal. To run a Gradle task from the terminal, you can either use Android Studio's terminal or use any of the terminals of your choice. Perform below command into the root of your KMPAndroidiOS project:

./gradlew :SharedCode:packForXcode

Tip: Sometimes this task may fail for you. If you see an error something like :SharedCode:compileKotlinIOS FAILED, try running following command on the terminal:

sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer/ while runing gradle task

Once you are able to successfully run the packForXcode task, you need to run the :build task from SharedCode. On terminal the command may look like the following:

./gradlew :SharedCode:build

Once this task is completed you will see .framework file being generated into the build directory of SharedCode. Refer to Figure 10.7 and have a look at the highlighted directory in the project explorer panel:

Figure 10.7

Once the framework is generated the 80% work is done. The Kotlin part is finished. Now onwards it's all about creating the iOS application and setting up a framework in Xcode. If you are not a seasoned iOS developer, you don't need to worry. I have hard work on any iOS project. But this would be still a super easy task for you.

Note: From this point onwards you will need a mac machine. We will be using Xcode which is an IDE for developing iOS applications. Xcode only runs on Mac OS. If you have a Mac OS you can install the Xcode from AppStore.

Open the Xcode and click on Create new Xcode project. Choose Single View Application, with the target iOS. Click Next. In the next screen, you need to provide some key information about your project including name, language preference, bundle identifier, etc. Refer to Figure 10.8 to know about what information I have used to create iOS application for me:

Figure 10.8

Once the project is created, the next step will be to create a simple UI. Once again refer to Figure 10.4 in this chapter, the right side image is referring to the iOS app. By clicking on Main.storyboard. Usually to design a UI will require understanding about Auto layout, Constraints, etc. But for now, you can simply drag and drop the elements in the storyboard and create a quick and dirty interface. Apple's developer site has a very nice tutorial about building basic UI. You can refer to that tutorial here https://developer.apple.com/library/archive/referencelibrary/GettingStarted/DevelopiOSAppsSwift/BuildABasicUI.html

Once the UI is ready now is the time to add a SharedCode.framework in Xcode. This is a crucial step. We will be pointing to the framework locally, so the path of the framework depends on the way you have created the project. I have created this iOS application inside the root of my Android project. Refer to Figure 10.7 it will give you some idea where I have created the project. Can you see the iOSProject directory in parallel to SharedCode and app module?

Now let's add the framework into the project. Figure 10.9 has some rectangles highlighted in it. Those rectangles are the steps that will take you towards adding the framework. Click on your project name and select the tab General. Scroll down a bit and you will see a section where frameworks and libraries can be added. Click on + and a dialog box will appear. Click on Add other and select the SharedCode.framework from

After this, we need to set the location of the framework into the framework search path. By doing this we are telling Xcode, where to look for the framework:

Figure 10.9

Go to Build Setting tab and you should search for Framework Search Paths as shown in Figure 10.10:

Figure 10.10

Once again I have highlighted areas where you should focus on Figure 10.10. The value you should provide for Framework Search Paths is $(SRCROOT)/../../SharedCode/build/xcode-frameworks. This indicates Xcode where to look for the .framework directory. The reference to the file path can depend on the way you have created the project. SRCROOT refers to the root of your iOS project. Our iOS project is in the root directory of the Android project. And SharedCode is in parallel to iOSProject and app module. So this way we need to go two-level back from iOS project and refers to SharedCode. Two dots (..) and File path (/) exactly does this.

Once this is done, we are now ready to do required changes in the code so that we can invoke the Kotlin code. Open ViewController.swift file. And set @IBOutlet for UILable and UITextField. This is required so that you can refer to the UI element from the storyboard. It's like findViewById for Android. After setting up @IBOutlet next job is to set up a text change listener on the UITextField. You can register the editingChanged event on fromCurrencyTextField using following code:

override func viewDidLoad() {

super.viewDidLoad()

fromCurrencyTextField.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)), for: UIControl.Event.editingChanged)

}

I have registered this event on viewDidLoad delegate method. While registering editingChanged event we are saying runtime to call a method textFiledDidChange method. Inside this method, we will perform our business logic and invoke the Kotlin code. Following piece of code demonstrate how to do that:

@objc func textFieldDidChange(_ textField: UITextField) {

let fromAmount = Float(textField.text!)

toCurrencyLabel.text = String(format:"%.4f", CommonKt.convertToGBP(fromAmount: fromAmount!)) + " GBP"

let conversionFee="Conversion rate 1 GBP = "+String(format:"%.4f",CommonKt.conversionRate)+"\nConversion fee for "+ActualKt.getPlatformName()+" is "+String(ActualKt.getServiceFee())

conversionFeeLabel.text = conversionFee

}

The ActualKt and CommonKt are your classes from the SharedCode.framework. The iOS compiler simply considering it just another framework. It will remain in the dark of our trick which Gradle task did. Run the application now and it should do the currency conversion for you.

Keeping SharedCode up to date

The app is running now. So what is missing now? Why are we not concluding and worrying about one more step? Well, we added the framework, but what if the framework is updated later on. iOS app will not get the latest functionality and features from the SharedCode. And we don't want to delete and set the reference to framework every time to keep pointing it to the latest framework. For this, we need to tell Xcode to run the Gradle task which prepares Kotlin code for the iOS application. Xcode lets you run any tasks in the form of the script before it builds the application. For this go to Build Phases. Click on + and select New Run Script Phase as shown in Figure 10.11:

Figure 10.11

Now you will see a section Run Script and an option to specify a shell command. Here we will point to our xcode-frameworks and it's Gradle task :SharedCode:packForXCode. Make sure you use the same path here which you used while performing the step shown in Figure 10.10. And also before you perform this step, you should have built the framework at least once, otherwise, the xcode-framework does not even exist:

cd "$SRCROOT/../../SharedCode/build/xcode-frameworks"

./gradlew :SharedCode:packForXCode -PXCODE_CONFIGURATION=${CONFIGURATION}

This step is shown in the following Figure 10.12:

Figure 10.12

Note: Take extra care while performing this step. Because once you configure this step, you need to reorder the Run Script phase. This phase should be first in order. You can do so by dragging the section to the top. Otherwise, it may not give you the desired result every time. We have to do it first because we need a framework to be available before we compile the sources.

Now here we are done! We are now ready to build and run the iOS application and see our Kotlin code working for us. EVERY TIME!

Some useful learning resources

Kotlin is lucky enough to get the love of the open-source community. The open-source community has also made a great contribution to Kotlin's growth. In the last 4 years, there are a lot of cool projects which can help you to accomplish your day to day job swiftly, accurately and in a quality way. I am dedicating this section to list down all those projects and say thank you to them.

Libraries

We already learned about libraries like koin, ktlint, mockk, and kodein, but there are more awesome libraries, some of them are listed below:

Ktor: https://github.com/ktorio Ktor is a framework for building servers and clients in a connected system using powerful Kotlin programming language.

RXKotlin: https://github.com/ReactiveX/RxKotlin Kotlin extension for RXJava

Spek: https://www.spekframework.org/ A specification and testing framework for Kotlin

Arrow: https://arrow-kt.io/ your functional programming companion to standard Kotlin library. Everything is a function here.

Kluent: https://github.com/MarkusAmshove/Kluent A cool looking assertions for your test.

Websites and blogs

Apart from Kotlin's official website and Android's developer portal, there are some one of the finest websites and blogs and projects which you should refer to. They are an incredible and amazing source of learning:

Kotlin Koans: https://play.kotlinlang.org/koans/overview one of the best ways to start learning Kotlin from the beginning is Koans. You can do it in your IDE or even in the browser.

Kotlin expertise: https://kotlinexpertise.com/ Blog by Simon Wirtz @s1m0nw1 is one of the best blogs I use for learning purposes.

Antonio's Blog: https://antonioleiva.com/category/blog/ One of the oldest and from one of the greatest Kotlin blog. The blog is from Antonio Leiva @lime_cl.

Kt.Academy: https://blog.kotlin-academy.com/ yet another resource to learn Kotlin in depth. The bog is from Marcin Moskala @marcinmoskala. The topics are interesting and unique compare to other websites.

Android Showcase: https://github.com/igorwojda/android-showcase The Android showcase project is the top-notch learning material you will find for Kotlin on GitHub. The project is owned and maintained by Igor Wojda @igorwojda.

Wrapping up

With this chapter, we are now coming to the end of this fantastic learning voyage. In this chapter, we learned about the Kotlin ecosystem. We learned about using Kotlin for iOS applications. Kotlin can be an amazing one-man army tool for all kinds of problems. It is very reliable. It has got great community support and maintained by giants like JetBrains and Google. And this is just beginning. Kotlin is still on a journey as a developing language. Even at this stage, it's a very mature language. What else you can expect from a language.

There is still a lot of topics to explore. The programming approach is constantly updating. The new architecture framework and approaches are still coming up. Android is also keeping its pace to meet the modern world's requirements. The blogs, websites, and projects which I have listed in the above section are maintained on a weekly and monthly manner. Keep referring to those incredible sources. I wish you all the best in your career development of becoming an Android application developer. Keep writing, keep developing, keep kotlin-izing, and keep smiling.