Home Programming Hands-On Object-Oriented Programming with Kotlin

Hands-On Object-Oriented Programming with Kotlin

By Abid Khan , Igor Kucherenko
books-svg-icon Book
eBook $39.99 $27.98
Print $48.99
Subscription $15.99 $10 p/m for three months
$10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
BUY NOW $10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
eBook $39.99 $27.98
Print $48.99
Subscription $15.99 $10 p/m for three months
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
  1. Free Chapter
    Getting Started with Kotlin
About this book
Kotlin is an object-oriented programming language. The book is based on the latest version of Kotlin. The book provides you with a thorough understanding of programming concepts, object-oriented programming techniques, and design patterns. It includes numerous examples, explanation of concepts and keynotes. Where possible, examples and programming exercises are included. The main purpose of the book is to provide a comprehensive coverage of Kotlin features such as classes, data classes, and inheritance. It also provides a good understanding of design pattern and how Kotlin syntax works with object-oriented techniques. You will also gain familiarity with syntax in this book by writing labeled for loop and when as an expression. An introduction to the advanced concepts such as sealed classes and package level functions and coroutines is provided and we will also learn how these concepts can make the software development easy. Supported libraries for serialization, regular expression and testing are also covered in this book. By the end of the book, you would have learnt building robust and maintainable software with object oriented design patterns in Kotlin.
Publication date:
October 2018
Publisher
Packt
Pages
370
ISBN
9781789617726

 

Getting Started with Kotlin

Kotlin's popularity has skyrocketed in recent months due to the fact that it is a simple and concise language that is easy to learn and supports object-oriented and functional programming. Kotlin is a superset of Java that avoids all unwanted Java features, such as verbose, unsafe, and outdated syntax, and includes powerful features from different languages. These features include security, simplicity, and interoperability.

This chapter enables us to acquire knowledge about Kotlin and introduces its basic features and functionality, aiming to get us started as quickly as possible. Before reaching the end of this chapter, we will be familiar with Kotlin's syntax, simplicity, and security features, as well as Kotlin's underlying approach towards object-oriented programming.

The following topics will be covered in this chapter:

  • Kotlin's history
  • Data types and ease of declaration
  • Kotlin's null safety and type casting
  • Flow control and if statements as an expression
  • Loops and labeled loops
  • Functions, functions as an expression, and named parameters
 

Technical requirements

IntelliJ IDEA is used as a development environment, either Community or Ultimate Edition. This can be downloaded here: www.jetbrains.com/idea/download.

Make sure that the latest Java Development Kit (JDK) is installed and configured in our system. Download the JDK here: www.oracle.com/technetwork/java/javase/downloads/index.html.

The code for this chapter can be downloaded here: https://github.com/PacktPublishing/Hands-On-Object-Oriented-Programming-with-Kotlin/tree/master/src/main/kotlin/Chapter01.

 

Introduction to Kotlin

In 2010, Jet Brains started working on a project called Project Kotlin. This aimed to develop a language that is easy, concise, and expressive. This is also a language that can help to improve productivity without compromising on quality, including that of backward compatibility and interoperability with existing code bases. Other than freedom from semicolons, there are a number of reasons to use Kotlin.

For a start, a beginner programmer can feel as comfortable as an experienced developer when using it. It also compiles to Java-6 bytecode, which allows a developer to use advanced features such as lambda expressions with legacy code. Kotlin is 100% interoperable with Java, so it is possible to call Kotlin code in Java as well as calling Java code in Kotlin. Furthermore, it is concise and expressive, and it helps avoid the boilerplate code that is required in Java. Kotlin is safe, therefore most Null Pointer Exceptions (NPEs) can be avoided. By default, it is not allowed to assign null values to variables. If the type of the variable is verified at compile time, the language is considered to be statically typed. Kotlin is a statically typed language and the benefit of this is that all tricky and trivial bugs can be caught at an early stage. With dynamically typed languages, type checking is performed at runtime. Perl, Smalltalk, and Ruby belong to a dynamically typed language group. Kotlin has great tooling support because it is a product of Jet Brains, a company renowned for providing IDEs for development. In addition, Kotlin supports Android because it is officially supported by Google. Kotlin supports Kotlin/Native technology for compiling Kotlin code in native binaries which does not rely on virtual machine and Kotlin supports browsers because all modern languages should work with JavaScript. A number of big brands (including Pinterest, Uber, Gradle, and Evernote) have started using Kotlin as a main language, and they feel that it helps to improve their productivity and quality of code.

With the help of Kotlin, we will now create our first hello world application as follows:

  1. Start IntelliJ IDE and click on File.
  2. Click on the New option and then click on the Project in the Menu.
  3. From the left pane in the newly opened window, select Kotlin | Kotlin/JVM and then press Next.
  4. Assign a project name and location, making sure that the latest SDK is selected.
  1. Click on Finish. IntelliJ IDE will open a new window with preconfigured files and folders. src is a source folder where all Kotlin files will be added.
  2. Right-click on src and select New. Under this, click on Kotlin File/Class.
  3. Assign a name, select a file from the Kind menu, and press OK.
  4. Add the following code in the newly opened window:
fun main(args: Array<String>) {
println("Hello world")
}
 

Data types in Kotlin

In the same way as any other modern language, Kotlin uses variables or data types, which are among the most important features in programming. These variables are created to handle different types of data, including numbers, letters, words, and so on. Variables are allocated locations in memory for storing all kinds of data.

The data types in Kotlin are as follows:

  • Byte
  • Short
  • Integer
  • Long
  • Float
  • Double
  • Character
  • Boolean

Declaring a variable in Kotlin

There are two keywords available in Kotlin—var and val. The var keyword declares a mutable data type and the val keyword declares an immutable or read-only variable.

The var keyword

The var keyword declares a mutable property or local variable. This means that the variable can be changed or updated as follows during the course of the entire program:

var age = 25

In the preceding code, var is a keyword, age is a variable name, and 25 is an assigned integer value. Let's see some other variable definitions to understand the declarations better:

  • var myChar = 'A': myChar is a single character variable
  • var name = "Bob": A name is a string type variable
  • var age = 10: age is an integer type variable
  • var height = 5.10: height is a double type variable

In Kotlin, a variable must have a value assigned. Without proper initialization, it does not allow variable declarations:

var age //In-Valid Declaration; compiler error
var age = 25
Valid Declaration

The value of the variable can be changed, but the variable type itself cannot be changed. If we try to re-assign an integer variable with double or string, the compiler will throw the mismatch error type:

var age = 10 // Data type integer - Valid declaration
age = 10.2 // Invalid assignment - not an integer compiler error
age = "hello" // Invalid assignment not an integer - compiler error

It is necessary that we declare different variables with the var keyword, as demonstrated here:

fun main(args: Array<String>) {
var student = "Bob" // String variable
var age = 25 // Integer variable
var height = 5.6 // Double variable
println("Name is $student age is $age and height is $height")
}

The val keyword

The val keyword is a read-only variable that is used to declare an immutable variable. Immutable means that once the variable is assigned, it will remain the same until the end of the application's life:

val age = 25 

In the preceding code line, val is a keyword, age is a variable name, and 25 is an integer value assigned.

val is the same as var; the variable type is dependent on the value assigned to the variable.

The read-only feature is actually one of the safety features provided by Kotlin.

Once the variable is declared, it is not possible to update it under any circumstances. This feature becomes more important when we are writing a complex application or a program of scientific calculations:

val pi = 3.14159

Knowing that the value of pi is constant, it would be a good practice to use val instead of var to make sure that the value remains constant and cannot be changed accidentally. Once declared as val, try to re-assign any other value to pi as follows:

pi = 123.345 // The result: Compiler will throw an error: "val cannot re-assign".

It is necessary that we declare different variables with the val keyword:

fun main(args: Array<String>) {
val name = "Herry" // String variable
val PI = 3.1415 // Double variable
val programmingLanguage = "Kotlin"
programmingLanguage = "Java" // Error: val cannot be reassigned
println("Name is $name and my favorite programming language is $programmingLanguage")
}

Type inference

Type inference is a mechanism in which the Kotlin compiler plays its role to find out a variable type. Kotlin will determine the data type by understanding the value assigned. It intelligently infers the type by the value and then makes the variable of the data type respectively. This technique is called type inference. In all the previous code, whether a data type is declared with val or var are examples of type inference:

val age = 25
val name = "Bob"

In the preceding code, age is an integer and name is a string type variable. The type of these variables is inferred by the compiler.

Type annotation

In Kotlin, we can also declare a specific type of variable with var variableName : nameOfDataType type annotation:

var myInteger : Int
var myString : String
val myDouble : Double

Once the variables are declared, we can assign values to them:

myInteger = 10
myString = "Hello"
myDouble = 12.123

Having variable declarations with explicit data types and assigning a value at the same time is also possible:

val myInt : Int = 10
var myString : String = "Hello"

Variable declaration and initialization is explained in the following example:

fun main(args: Array<String>) {
var myName: String // Variables can be initialized explicitly
myName = "Jon" // Initialization
// declaration and initialization in one line
var myInt: Int = 10
var myLong: Long = 11
var myShort: Short = 11
var myByte: Byte = -128
var d1 = 5.10 // Declaration of Double and Float
var d2: Double = 5.10
var f1 = 5.10
var f2: Float = 5.10f
}

Kotlin data types are divided into the following groups:

  • Number data types
  • Real data types
  • Boolean data types
  • Character data types

Now let's have a look at each category in the following sections.

Number data types

Number data types accept whole numbers and do not support fractions. Kotlin provides four different types of number variables—Integer, Long, Byte, and Short. These data types can be declared with and without type annotation. It can also be easily declared with the following syntax:

var myInt : Int =10  // For Integer variable
var myInt = 10
var myLong : Long = 11 // For Long Variable
var myShort : Short = 11 // For Short Variable
var myByte : Byte = -100 For byte variable

MAX_VALUE and MIN_VALUE can be used to find the capacity of each variable.

See the following example:

fun main(args: Array<String>) {
println("max integer " + Integer.MAX_VALUE)
println("min integer " + Integer.MIN_VALUE)
}

The following output shows the maximum and minimum value an integer can hold:

Real data types

In this category, Kotlin provides two data types:

  • Float
  • Double

These data types can store values containing decimal places. Float and Double are the floating point data types that are used to store real number values. A Float can contain 4 bytes of information, while the Double data type can handle 8 bytes. Kotlin allows us to handle scientific notation with the Double data type as follows:

  1. Create two Double types of variables as follows:
  • Create d1 with an explicit declaration of the data type
  • Create d2 with an implicit declaration:
fun main(args: Array<String>) {
var d1 : Double = 7.20E15 // Scientific calculation
var d2 = 7.20e-15
  1. Assign scientific values to both variables and print:
println("Value of d1 = " + d1 + " and Value of d2 = " + d2)
}

The Value of d1 = 7.02E15 and Value of d2 = 7.02E-15 printed values are the same, except the value of d2 is capitalized.

Boolean data type

Boolean is a data type that contains one of two values—true or false. Because of its nature, the Boolean variable requires only one single bit for storing data. This bit can be on or off, true or false, 0 or 1. Let's declare some Boolean variables for further discussion:

var result : Boolean = true
var isEmpty : Boolean = false

In the same way as other variables, the Boolean variable can be declared without type inference:

var value = false
var result = true

Basically, this data type is used for comparing two values, getting results by setting a Boolean as a checkpoint to verify the results via comparison, and getting an answer by way of true or false. Let's see an example of how this can happen:

fun main(args: Array<String>) {

var result : Boolean // Boolean variable
var num1 = 20
var num2 = 10

result = num1 >= num2
println("$num1 is greater than $num2 = $result")

result = num1 < num2
println("$num1 is greater than $num2 = $result")
}

In this example, we have two integer variables named num1 and num2, as well as a Boolean variable named result. If num1 is greater than or equal to num2, true will be assigned to result; otherwise, false will be assigned.

Character data type

The character data type is one of the data types available in Kotlin. This can contain 2 bytes of information and it can also store pretty much all the characters that you see on your keyboard. The syntax is the same as any other declaration—the word Char is used to declare a character variable and the value on the right-hand side must be enclosed by single quotes:

var mychar : Char = 'A'

Let's take a look at some examples. When character values are assigned to the Char data type, this can be displayed as follows:

var charA : Char = 'A'
var charZ : Char = 'Z'
var char1 = '1'
var char0 = '0'

Each character has a unique Unicode for its representation, and the character data type can be stored in Unicode values. Let's display A, Z, 1, and 0 by using a Unicode character. The syntax for storing the Unicode is pretty much the same, except \u is required at the beginning of the code. This is demonstrated as follows:

var ucharA : Char = '\u0041'
var ucharZ : Char = '\u005A'
var uchar1 = '\u0031'
var uchar0 = '\u0030'

The following code shows how a character data type handles different characters:

fun main(args: Array<String>) {
var charA : Char = 'A'
var charZ : Char = 'Z'
var char1 = '1'
var char0 = '0'
println("$charA $charZ $char1 $char0")

// Unicode Character
var ucharA : Char = '\u0041'
var ucharZ : Char = '\u005A'
var uchar1 = '\u0031'
var uchar0 = '\u0030'
println("$ucharA $ucharZ $uchar1 $uchar0")
}

Type checking with the is keyword

Type inference is one of the most powerful features in Kotlin, but sometimes it becomes hazardous when the type of variable is unknown. For example, we are asked to write a function that can take variable of the Any type. This can be float, string, or int:

fun func(x: Any) { 
// What is the type of x
}

To handle this tricky situation, Kotlin provides an is keyword to verify the variable type. The syntax for this is as follows:

x is Int
x is Char

This check will return true if x is an integer or character; otherwise, it will return false. Check the following examples:

fun func(x: Any) { 
if(x is Float){
println("x is Float")
} else if(x is String){
println("x is String")
}

!is can be used to verify whether or not a variable is a required type:

fun func(x: Any) { 
if(x !is Float){
println("f is not Float")
}
}

String variable

A string is a well-structured set of characters, words, sentences, and paragraphs. String is a widely used data type in Kotlin. Unlike other variables, such as Integer, Float, or Boolean, which contain one specific type of value, Strings can contain a collection of different values and can store pretty much everything and anything.

Like other Kotlin data types, String variables can be declared with or without type inference, and everything within double quotes will be considered as a string:

var variable_name : String = "value in double quotes"
var message : String = "Hello"
var question : String = "What is your name?"

A string variable, message, is initialized with Hello, and another string variable, question, is assigned with What is your name?. A string variable can be declared with the var or val keyword without stating the variable type, for example, val name = "Bob":

fun main(args: Array<String>) {
var message : String = "Hello"
var question : String = "What is your name?"
println(question)
val name = "Bob"
var address = "Stockholm, Sweden"
println("My name is $name and i live in $address")
}
 

Null safety, type casting, Pair, and Triple

In this section, we will learn about one of the most important topic of Kotlin, which is null safety. We will learn how null safety works, why it is important, and how Kotlin helps to improve the code quality. Later in this section, we will learn about type casting and its implications and we will conclude this chapter by discussing to useful data structures Pair and Triple.

Null safety

Nullability is one of the reasons that most applications crash. Kotlin is very strict when it comes to safety. Every application user (especially mobile users) want a nice, simple, and smooth user experience. An application that crashes makes more than 90% of users frustrated, causing them to instantly uninstall the application.

See the following example of Java. Here, the variable name is assigned a null value, which is the correct syntax in Java:

String name = null; // OK in java
int length = name.length(); // application crashed

But when name.length will be executed, Java will throw NullPointerException. In Kotlin, it is very important to note that variables are non-nullable by default and we cannot assign null values to them. Let's take a look at an example and assign a null value to a variable as follows:

var notNull : String = null

If we try to assign a null value to a variable, the compiler will immediately throw an error:

Error: "Null cannot be a value of a non-null type String".

At the time of declaration, a value must be assigned to the variable as follows:

var notNull : String = "Hello"

It is now safe to use the length function. The length is a function provided by Kotlin that returns the length of a string:

var length = notNull.length

Kotlin is compatible with Java, and we can write both Kotlin and Java code in one application (see Chapter 8, Interoperability). Java is not a null-safe language, and because of this, Kotlin designers enable programmers to assign null values by defining a nullable variable:

var mayBeNull : String? = null

Adding a question mark to a command indicates to the compiler that a null value can be assigned to the variable:

fun main(args: Array<String>) {
var notNull : String = "Hello"
notNull = null // not allowed

var len = notNull.length
println("Value is $notNull and length is ${notNull.length} ")

var mayBeNull : String?
mayBeNull = null // allowed
}

Safe call operators

Now we are able to declare a nullable type, but what if we try to get a length of a string that is nullable? See the following example:

var mayBeNull : String? = null
var length = mayBeNull.length

Kotlin null safety will trigger the following error:

Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?

In simple terms, a programmer will be notified that the declared variable (string in this case) can have a null value, and this must be verified before calling. It can be validated in different ways, but one way of doing this is by using an if statement:

var mayBeNull : String? = null
if(mayBeNull != null && mayBeNull.length > 0){
var length = mayBeNull.length
}

Within the if condition, notice that Kotlin does not throw any errors. The if statement will be executed if the variable has a value, otherwise it will be skipped:

fun main(args: Array<String>) {
var name : String?
name = null // allowed
var length = 0
if(name != null && name.length > 0) {
length = name.length
}
}

In this example, we have declared a nullable string variable name and assigned a null to it. Later, we check if the name string variable is not null and length of the name variable is not zero. In this case, an if statement will skip the code block because name is assigned with null.

The ?. Safe call operator

An alternative way to verify the nullable variable is by using the safe call operator, which is a question mark followed by a full stop, ?.:

var length = mayBeNull?.length

In this case, if the string variable is not null, the safe call operator will return the length of the variable. Otherwise, null will be assigned to the length variable. This means that when the if variable is null, everything after the ?. operator will be ignored:

fun main(args: Array<String>) {
var mayBeNull : String?
mayBeNull = null // allowed
var length = mayBeNull?.length // Safe Call
println("value of length is " + length)
}

The output of this example is "value of length is null" because the safe call operator verified the mayBeNull variable and returned a null value.

The ?: Elvis operator

The safe call operator executes the called function if a variable contains a value. If not, it will return null. This is demonstrated as follows:

var mayBeNull : String? = null
var length = mayBeNull?.length

If the length variable has a null value, we again need to verify whether the variable length is null or not. In this case, we may be stuck in an unnecessary verification loop. This problem can be solved by using the Elvis operator. The Elvis operator makes sure that one out of two values must be returned:

var length = mayBeNull?.length ? : 0

If length is not null, it will return the size of the variable; otherwise, it returns 0. See the following example:

fun main(args: Array<String>) { 

var message: String? = null

var len = message?.length ?: 0
println("value of length is $len")

message = "Hello"
len = message?.length ?: 0

println("value of length is $len")
}

Create a nullable string variable called message and assign a null value to it. Use the Elvis operator, call the length function, and verify the value of the len variable, which should be 0. Now assign a value to message and verify the length.

The !! Sure operator

The not null assertion operator, also known as the sure operator, is used when it is sure that the provided variable always contains a value and is not null. Let's take a look at an example of how this works. Create a nullable string variable and assign a null value to it. Now try get the string's length with the null assertion operator:

fun main(args: Array<String>) {
var sureNotNull : String? = null
var length = sureNotNull!!.length // application will be crashed
println("value of length is " + length)
}

Of course, the application will crash. In this case, the programmer takes responsibility for variable nullability. Let's elaborate on this further with the another example.

The string class provides the lastOrNull function. This function returns the last character of the string, or null if the string is empty. We must declare a nullable character for assigning a value from the lastOrNull function:

val ch : Char? = "abc".lastOrNull()

If we try to declare a normal variable instead of nullable, Kotlin will throw a compile time error. See the following example:

val ch : Char = "abc".lastOrNull()
// Type mismatch: inferred type is Char? but Char was expected

If we are confident that an object (the "abc" string, in this case) is not null and we don't want to create a nullable variable with the null safety operator, we can use the null assertion operator:

val ch : Char = "abc".lastOrNull()!!

See the following example to verify how variables can be declared with and without the null safety operator:

fun mayBeNull(s : String ) : Char? {
val ch: Char? = s.lastOrNull()
return ch
}

fun notNull(s : String ) : Char{
val ch = s.lastOrNull()!!
return ch
}

fun main(args: Array<String>) {
var ch = notNull("abc")
// var ch = notNull("") program will crash.
println(ch)
}

The myBeNull function takes a string as a parameter and returns the last character of the string using the lastOrNull function. Notice that the function returns a nullable Char because function may receive an empty string as a parameter. On the other hand, the notNull function returns a normal character because this function uses the not null assertion operator to get the last character of the string, and we tell the compiler that this function will never receive an empty string.

Type casting

Converting data from one type to another is called type casting, for example, conversion from Float to Integer, or from Double to String. With Java or C++, type casting is a very straightforward process because these languages have primitive data types. Look at the following Java example:

double d = 10.50;
int i = (int) d;

In Kotlin, everything is an object and so it requires some extra steps to type cast from one type to another. However, Kotlin provides a rich library that helps perform these conversions. Let's explore this with the following example. Create a Byte variable and assign a value of 10 to it. Create an Integer variable and attempt to assign byteValue to intValue:

var byteValue : Byte = 10
var intValue : Int
intValue = byteValue

Since the data types are different, the preceding code block will throw a type mismatch error caused by the compiler. Using the following line will not help either:

intValue = (Int) byteValue

The preceding example shows that Kotlin does not support automatic type casting, so we will have to invoke it explicitly. Kotlin's library is packed with a number of useful functions, and each data type can use these functions for type conversion.

Converting from Byte to Float

Create a Byte variable and assign a value to it, then use the toFloat() function to convert this from Byte to Float as follows:

fun main (args: Array<String>) { 
var byteValue : Byte = 10
var floatValue : Float
floatValue = byteValue.toFloat()
println("From Byte $byteValue to Float $floatValue")
}

Converting from Double to Integer

Similarly, we can convert a Double variable into an Integer. Use the toInt() function to convert from Double to Int as follows:

fun main (args: Array<String>) {
var doubleValue : Double = 12.345
var intValue = doubleValue.toInt()
println("From Double $doubleValue to Int $intValue")
}

When converting from one type to another, we must take data loss into consideration. The following is the output of this example:

From Double 12.345 to Int 12

Double belongs to the real data type family. It can store values containing a decimal point. In contrast, Integer belongs to the number data type, which deals with whole numbers only. When the Double data type is converted to an Integer, the intValue variable simply ignores the .345 fraction value.

Converting from String to Integer

It is also possible to cast from String to Integer or from String to Double. To do this, create a String variable and cast it by using the toInt() function. See this example:

fun main (args: Array<String>) {
var
stringValue: String = "125"
var intValue = stringValue.toInt()
println("From string to int $intValue")
}

Everything is fine if the String variable contains a valid integer value, but if the String variable contains anything other than integer, Kotlin will throw a NumberCast exception. Update the following stringVariable in the previous example and verify the exception like so:

var stringValue : String = "A125"

To avoid this situation, Kotlin provides the toIntorNull function. This function will return a null if the String variable has an invalid value, such as an alpha-numeric character, whereas it will cast a string to integer if the value is numeric. It is also important to mention here that the toIntOrNull() function can return a null value, and thus the integer variable must be nullable when declared as Int?. See the following example. Create a string variable and convert it into string by using toIntOrNull function:

fun main (args: Array<String>) {
var stringValue : String = "125A"
var intValue : Int? = stringValue.toIntOrNull()

if(intValue is Int) {
println("From string to int $intValue")
}else{
println("Not a valid String")
}
}

If the string variable contains valid content, then conversion from String to Int will be successful otherwise false.

Smart cast

Any is a parent or a superclass of all classes in Kotlin. If our class is not derived from any class, then it has Any as a super class. All data types including Integer, Float, Double, and so on are derived from an Any class. (We will learn more about this in Chapter 3, The Four Pillars of Object-Oriented Programming). The following declarations are valid in Kotlin:

var any : Any? = null
any = 1234 // integer
any = "Hello" // String
any = 123.456 // Double

To understand the importance of smart casting, let's create a function with one parameter of the nullable Any? type:

fun mySmartCast(any :Any?)
{
if(any is Int)
{
var i = any + 5
println("Value is Int $i")
}
else if(any is String)
{
var s = "Hello " + any
println("Value is String $s")
}
else if (any == null) {
println("Object is null")
}
}

fun main (args: Array<String>) {
mySmartCast(8)
mySmartCast("Kotlin")
}

In the first function call with the integer value, the mySmartCast(8) smart cast not only takes care of the null type but also recognizes which type of class object it contains. Type checking, null safety, and unwrapping the object is handled by using the is operator. In the first if statement, the is operator verifies the null value and performs type casting as well.

Any is a superclass in Kotlin's class hierarchy.

Kotlin automatically converts Any into an integer to perform a mathematical operation on it, and we do not need to call toInt() function for type casting. Here is a smart cast example with the when expression:

fun mySmartCast(any :Any?){

when(any) {
is String -> println("String: $any")
is Int -> println("Integer: $any")
is Double -> println("Double: $any")
else -> println("Alian...")
}
}

Unsafe cast

The as operator is another method of type casting, but it is not considered a safe cast. Check out the following example of an unsafe cast:

fun myUnsafeCast(any : Any?) {
val s : String = any as String
println(s)
}

fun main (args: Array<String>) {
myUnsafetCast("Hello")
}

This code will execute successfully because a string variable is passed to this function, but the following function calls will throw a TypeCastException:

myUnsafetCast(2)
myUnsafetCast(null)

It is very important to secure our code before it crashes, so try to avoid unsafe casting. However, if it is necessary, do the following:

  • Declare a nullable variable with ? to store the value
  • Add a safe call with the as operator as ?

If the type casting is successful, it will return the original value. If not, it will become null. Let's take a look at the correct way to use the as operator in the following example:

fun myUnsafeCast(any : Any?){
val s : String? = any as? String
println(s)
}
fun main (args: Array<String>) {
myUnsafetCast(2)
}

This time, the program will execute normally without throwing any exceptions. Instead, it will display null on the screen.

Pair and Triple

Pair and Triple can store different values that are closely linked to each other, for example, a product name and price, x and y coordinates of a graph, or a phone book with a name, phone number, address, and so on. We can store these values by declaring a class and combining them in one object, but it is always good if a similar task can be performed by using pre-declared Kotlin classes. In this section, we will take a look at how to use Pair and Triple and how these data types help to organize different values in one place.

How to declare

Let's start by declaring Pair and Triple. Like other variables, Pair and Triple can be declared by using the val or var keyword:

val mobile = Pair("Google", 500)
val screenMirror = Pair("Chrome cast", 20.5)
val addressBook = Triple("Khan", 123456789, "Stockholm")

First, create a variable by directly assigning some values with Pair or Triple. As we can see, each Pair and Triple contains different data types—a mobile Pair contains a string and integer set, screenMirror contains a string and a double, and an addressBook Triple contains two strings and one integer. We can see that an explicit declaration of a data type is not required, as the Kotlin type inference automatically finds out the variable type.

How to retrieve values

There are plenty of ways to retrieve values from Pair and Triple, but let's start with simple one. The value of a Pair can be retrieved by assigning it to the following variables:

val (name , price) = mobile

The name variable is assigned with Google and price contains 500 Euros, which is the price of a Google mobile. We can verify this by printing these variables like so:

println("Mobile = $name , Prince = $price Euro")

A Triple can be deconstructed in a similar fashion:

val (name, phone, address) = addressBook
println("Name = $name , Phone = $phone , Address = $address")

There is another way to decompose the Pair and Triple classes. Each member of the Pair and Triple is assigned a name. The first element of the Pair can be accessed by using the property name first, the second with second, and in a Triple, the third element can be accessed with the property name third. For example, create a Pair and Triple of different types. Assign and retrieve the values as follows:

val mobile = Pair("Google", 500)
val
(name , price) = mobile
println("Mobile = ${mobile.first} , Prince = ${mobile.second}")

val addressBook = Triple("Khan", 123456789, "Stockholm")
val
(name, phone, address) = addressBook
println("Name = ${addressBook.first} , Phone = ${addressBook.second} , Address = ${addressBook.third}")

Kotlin also provides a default function for each element—component1() for the first element, component2() for the second element, and so on:

val (p_name, p_phone, p_address) = addressbook
println
("Name = ${addressbook.component1()} , Phone = ${addressbook.component2()} , Address = ${addressbook.component3()}")

While retrieving these values, if any are not required, we can ignore them by using the underscore symbol. See the following example:

val coordinates = Triple(5 , 9 , 11)
val (x, y , _) = coordinates

The coordinates variable contains three values, but by using the underscore symbol, we have simply ignored the z coordinate.

 

Flow controls

Programming is the execution of different operations, and the flow control is an operation to control the execution of these operations. By using this, a programmer can decide how a program should behave or what section of code should be executed at a given time. Like other programming languages, Kotlin provides several structures that allow the control to be implemented.

We will now explore each of the flow controls in the following sections.

The if statement

The if statement can be perceived as a filter method that is designed to channel the relevant data, and further operate, drive, or act on that information.

As with most programming languages in common practice, there are code blocks that are dependent on certain parameters, variables, and conditions that may only execute when a certain situation is true or false. if statements are used when a decision needs to be made; this works with Boolean logic, which means that the conditions defined for an if can only have two outcomes, true and false:

if (a > b) {
max = a
}

When an if statement is present and the condition is true, the primary code block is executed. If not, the code block is ignored, as we can see in the following example:

fun main(args: Array<String>) {
val langName = "Kotlin"
if ( langName == " Kotlin" ) {
println ( "Hello"+ langName)
}
}

The result would display as Hello Kotlin. When the value of the langName variable is anything other than Kotlin, the code block under the if statement would not be executed.

The if and else statements

The if and else statements have been around for a long time, and almost all languages are dependent on these types of conditional statements for filtering information. When the condition is satisfied, the primary code block will be executed. If the condition fails, the else statement comes into play.

Let's see an example of how this works:

fun main(args: Array<String>) {

val langName = "Java"
if ( langName == "Kotlin" ) {
println ( "I love "+ langName )
}
else {
println ("The name of the language is "+ langName )
}
}

If the langName variable is assigned a "Kotlin" string, then the first code block of the if statement will be executed. If not, the else statement will be executed.

The if statement with a conditional operator

When we work in real time, more often than not the if statement is used to break down complex scenarios and define the flow without getting into the hassle of checking each individual condition separately. To reduce code complexity and encourage better implementation of program flow, we can use conditional operators to combine or group prerequisites into a single if condition to filter and execute the relevant code block.

The conditions can be combined or grouped in an if statement by using the and (&&), or (||) and not(!) operators.

The if statement with the and (&&) operator

The and (&&) operator is used for absolute results. The and operator returns true if and only if all conditions grouped together are satisfied. In the event that either conditions fail to satisfy the grouped condition, the if statement will fail and the respective code block will be ignored until the grouped condition is satisfied as a whole.

An if statement with an && operator can be written in ampersand form:

var studentMarks = 92
if (studentMarks >= 90 && value < 96) {
println ("A")
}

The and operator can also be written in word form:

if ((studentMarks >= 90) and (value < 96)) {
println ("A")
}

The if statement with the or (||) operator

The or (||) operator is a fairly lenient operator which returns true when either of the two conditions in the group is true. The if statement fails when both conditions are not satisfied and skips the respective code block until one of the grouped conditions is satisfied. The if with || operator can be written in symbol form:

if ((b > a) || (b > c)) {
println("b is a winner")
}

The or operator can be written in word form:

if (b > a or b > c) {
println("b is a winner")
}

The if statement with the (!) Not operator

The Not operator, represented by an exclamation mark, !, is used when the condition with a NOT operator is true if the returning value is false. The Not operator also makes it possible to easily check if the condition is not true, in which case, the following result should be displayed:

if(a!=b) {
println("a and b are different")
}

if as an expression

Kotlin has introduced a new feature called if as an expression, which makes a programmer's life much easier. Instead of assigning a value in each if statement, Kotlin returns the value from a successful code block, which can be stored in a variable. Before writing an if statement, add a variable name with an assignment operator as follows:

grade = if (studentMarks >= 90) {
"A"
}

See the following example with if as an expression, where grade will be assigned depending on studentMarks:

fun main(args: Array<String>) {
val studentMarks = 95
var grade = if (studentMarks >= 90) {
"A"
} else if (studentMarks >= 80) {
"B"
} else if (studentMarks >= 70) {
"C"
} else if (studentMarks >= 60) {
"D"
} else {
"F"
}
println ( "Student achieved " + grade )
}

Notice that it is not required to write grade = "A" or grade = "D" in each else…if block, but while using if as an expression, there is one thing to remember—if as an expression cannot be used without an else statement:

val grade = if (studentMarks >= 90) {
"A"
}

Kotlin will throw the following compile-time error:

'if' must have both main and 'else' branches if used as an expression

The When expression

Kotlin provides an alternative method to the if statement—the When expression. When can also be perceived as a filter method. This is similar in nature to the Switch statement in Java or C. When sequentially matches its arguments with all branches until a condition is satisfied for a branch.

The When expression works as follows:

  • When can use arbitrary expressions and constants.
  • It takes the variable in the expression and matches the value within the branches.
  • If the condition for the variable is a match, the relevant code block of the branch will execute. If none of the other branch conditions are satisfied, the else branch is evaluated.

Writing the When statement:

  • The When statement is followed by the expression defined within the parenthesis: when (expression).
  • A branch is a condition followed by a code block. This is defined as {condition -> code block} and appears after the expression contained within the curly brackets.
  • The else branch is mandatory with its own code block contained within the curly brackets.
  • If there is no else branch in the when, all possible cases must be covered in the branches so that the compiler can validate all of the branches.

Write a program by using when as an expression to display the day on its corresponding number, 1 for Monday, 2 for Tuesday, and so on:

fun main(args: Array<String>) {
val day = 2
when(day) {
1-> println("Monday")
2-> println("Tuesday")
3-> println("Wednesday")
4-> println("Thursday")
5-> println("Friday")
6-> println("Saturday")
7-> println("Sunday")
else -> println("Invalid input")
}
}

To verify and test various conditions, assign different values to the day variable. The program will display the respective day according to the value or error message if the input is out of range. We can rewrite the student grade program by using the when expression.

Combining cases

The When expression allows us to combine more than one cases in one line. In order to match with the expression, we concatenate more than one case in a comma-separated list:

fun main(args: Array<String>) {
val grade = "b"
when (grade) {
"A","a" -> println("Excellent")
"B","b" -> println("Very Good")
"C","c" -> println("Nice work")
"D","d" -> println("OK")
"E","e" -> println("Hmmm")
"F","f" -> println("Better luck next time")
else -> println("Invalid input")
}
}

In this example, a user can enter the student's grade without worrying about whether the keyboard caps lock is on or off. If input is "a" or "A", either way the output will be Excellent.

Ranges with When

It is also possible to match cases in range form. To make use of ranges, Kotlin provides the in operator. Using the in operator, we are asking for a value that is contained within a given range. This is specifically useful when more than one condition has the same result:

fun main(args: Array<String>) {
val grade = "A"
when (grade) {
in "A".."E" -> println("You are promoted to the next level")
"F" -> println("You need hard work.")
else -> println("Invalid input")
}
}

In this example, if the student grade is within the range of A to E then a You are promoted to the next level message will be displayed. If the grade is F, then You need hard work. will be displayed on the screen; otherwise, Invalid input is displayed.

When as an expression

Similarly to the if statement, when can also be used as an expression. To do this, create a variable and assign when as an expression as follows:

fun main(args: Array<String>) {
val grade = "A"
val remarks = when (grade) {
"A","a" -> "Excellent"
"B"
,"b" -> "Very Good"
"C"
,"c" -> "Nice work"
"D"
,"d" -> "OK"
"E"
,"e" -> "Hmmm"
"F"
,"f" -> "Better luck next time"
else -> "Invalid input"
}
println(remarks)
}
 

Introduction to loops

A loop is a cyclic code, routine, or statement that is defined once but may run several times, and can perform a set of instructions indefinitely or repeatedly (once for each of a collection of items, or until a condition is met). If we were asked to print numbers from 1 to 3 on the screen, this could have been done easily by writing the code using the three println statements. If we were to print hundreds or thousands of numbers, for example, we would need a better solution for performing one task repeatedly for as long as required. Loops are a great solution for these situations, and they are dependent on three parts:

  • Start: Defining the beginning of the loop
  • Body: Defining the code block to execute it on each iteration
  • Controller: Defining when the loop should stop

In Kotlin there are two type of loops available:

  • Condition-controlled loop:
    • While loop
    • Do while loop
  • Count-controlled loop:
    • For loop

The while loop

A while loop is a statement or code that executes repeatedly based on a given condition. The while loop checks the condition before the block is executed. Similar to an if statement, the condition is assessed to see if the condition is true, and if so, the code within the block will execute, and the process will repeat until the condition becomes false. The while loop is useful when we want to perform one task as long as the condition remains true. Let's take a look at how to write a while loop in the following sections.

The while statement is followed by the condition defined within parentheses—( condition ).

Defining the while loop

The construct of while is similar to an if statement. Both of these work with conditions before executing the code block within. See the following example. Here, an if statement was written with a simple print line statement:

if(i <= 3) {
println("Print $i")
}

while(i <= 3) {
println("Print $i")
}

Once you have replaced the if with the while statement, the while loop is ready to use. However, do not forget to increment the value of i, otherwise the while loop will execute forever:

fun main(args: Array<String>) {
println("While loop")
var i = 1
while (i <= 3) {
println("While $i")
i++
}
}

This loop will execute three times and it will increment the value of i by one on each iteration. On the fourth iteration, the value of i will be 4 and the controlling statement will become false.

The do while loop

A do while loop executes a block of code at least once, and then repeatedly executes the block (or not) depending on a given condition at the end of the block. This is a minor variation of the while loop. In do while, the body executes before the condition is verified, therefore executing the code block at least once:

fun main(args: Array<String>) {
println("Do While loop")
var j = 1
do {
println(j)
j++
} while( j < 5 )
}

In this example, variable j is initialized with value 1, the do block prints the value of j and increments it, while block verifies the condition. The loop will continues until the value of j is less than 5.

The for loop

A for loop is used to specify an iteration that allows code to be executed repeatedly. This is a famous loop because of its flexibility and convenience. The for loop works with lists, arrays, collections, or ranges; it can be a range of integers or a collection of objects. The for loop also requires a control variable with start and end values, iterates on a given range, and exits the loop automatically. In a nutshell, it takes care of most things that other loops are not able to take care of.

Defining the for loop

The following points are necessary to create a for loop:

  • Declare a range with a start and end point. Ranges are defined with two dots, .., for example, var range = 1..3
  • With a for loop, create a variable and assign a range with in operator.
  • Define a code block that will execute a task:
fun main(args: Array<String>) {
var range = 1..3
for (i in range) {
println("value of $i")
}
}

On the first iteration, the for loop initializes the i variable with the first value of the range, and on each iteration the next value from the range will be assigned to i.

Any object that has an iterator function implemented can be used inside a for loop, for example, range, list, array, and so on.

With each iteration, the for loop assigns the next member from the range, which can be utilized as a normal member variable. In this example, each value from the range is printed on the screen:

fun main(args: Array<String>) {
val list = listOf(1,2,3,4)
for (l in list){
println("value of $l")
}

val message = "kotlin is awesome"
for (m in message){
println(m)
}
}

The for loop with ranges and iterator will be discussed in Chapter 5, Data Collection, Iterators, and Filters.

The nested for loop

A for loop within a for loop is called a nested for loop. The outer for loop will assign a value from range to i, and the inner for loop will assign a value from range to j, and both values will be printed in the inner for loop:

for (i in 1..3) {
for (j in 1..3) {
println("$i , $j")
}
}

Notice that on each iteration of the outer for loop, the inner for loop will be executed three times.

Break statements

Kotlin provides break statements, which are used to break the continuation of a loop. Break statements immediately terminate the iteration of a loop when the test condition is met. This is often used in while, do while, and for loops in order to end the current loop and exit where conditions may need to be defined inside the loop for specific reasons:

fun main(args: Array<String>) {
for (i in 1..10) {
println("For $i")
if(i >= 5) {
break;
}
}
}

Notice that the break statement terminates the program execution where it is placed. If the break statement is in an inner loop, the outer loop will perform its task normally. This is because only the inner loop will end and the outer loop will continue its iterations as defined until it fulfills its condition. Take an example of a nested loop and print the value of i and j but break the inner loop when both values are same:

for (i in 1..3) {
for (j in 1..3) {
println("$i , $j")
if(i==j) {
break;
}
}
}

The break statement with labeled for loop

Break statements always break the nearest or parent loop where the break is placed, as we saw in previous example. But what if we want to stop the loop iterations altogether whenever a certain condition is met in a nested inner loop? To address such conditions, Kotlin provides a concept called a labeled for loop. This means that an alias name is assigned to a for loop to break it by using the break@nameOfTheLoop statement. Assign a name to the for loop, as described here, and call the loop by using the break statement. When the variables i and j are equal to 2, by using break@outLoop, the break statement would know which for loop to terminate:

fun main(args: Array<String>) {

println("Labled For Loop")
outLoop@ for (i in 1..3) {
for (j in 1..3) {
if(i==2 && j==2) {
break@outLoop
}
println("$i , $j")
}
}
}

So, the outer for loop would run once and the inner for loop would execute its body three times. On the second iteration of the outer loop, when both i and j would equal 2, the break statement would call the outer loop to terminate. So, if the outer loop terminates, this means that the inner loop terminates automatically.

 

What is a function?

As a program grows, complexity grows. If the code cannot handle this growth, it is easy to become bogged down in the complexity of application. The best way to manage our code is to break it down into small, self-contained portions and the complex problem can be solved by putting these portions together. Kotlin can help us to divide our code into small chunks, and we can then assign a meaningful name to our code. That block can perform one particular task for us. In different programming languages, this technique is called a method, subroutine, or procedure. In Kotlin, this technique is called a function.

There are several reasons for dividing code into functions:

  • Divide and conquer: A programmer can solve a complex task by dividing it into small functions.
  • Reusability: Pasting similar code in different places is not a good approach. In the future, if a program's logic changes, we must update the pasted code everywhere else. Functions help us to reuse code anywhere in our program, and if the function code changes, it will have an effect in all areas.
  • Debugging: With big, complex problems, if the code does not work as expected, it is often difficult to find the hidden bug in spaghetti code. Without well-defined functions, it is a difficult, frustrating, and time-consuming task to fix the problem. If everything is divided into functions, a coder can test each function one by one in order to confirm its output.
  • Abstraction: In order to use a function, it is enough to know its name and parameters. The programmer does not need to know how it is implemented and what logic is used by another programmer.

Function declaration

Kotlin is a fun programming language, and so the function name begins with the word fun. The fun statement is followed by the function name with parentheses ( ). The code block is defined after the parenthesis within the curly brackets as { code block }. Once we have finished writing our code block, we can call this function from anywhere by using nameOfTheFunction. Writing hello function displays a greeting message on the screen. See the following example:

fun hello(){
println("Hello from Kotlin")
}
fun main(args: Array<String>) {
hello()
}

A simple Hello from Kotlin message will now display on the screen.

Functions with no parameter and no return type

This is the simplest form of a function. See the following example:

 fun sayHello(){
println("Hello from Kotlin")
}

If a function does not return a value, the Unit keyword can be declared right after the function name.

Unit corresponds to the void type in Java.

The Unit keyword is optional. If no keyword is mentioned, Kotlin will consider Unit as a default value:

fun sayHello() : Unit{
println("Hello from Kotlin")
}

Function with parameters

Functions can receive one or more parameters as arguments:

fun hello(message : String) : Unit {
println("Hello from $message")
}

fun main(args: Array<String>) {
hello("Kotlin")
}

The hello function takes a string variable as a parameter. When the hello function is called from main, a string value is passed to that function.

If the function takes more than one parameter, all parameters are separated with a comma (,). Here is another example of writing a function that takes two parameters:

fun add(a : Int, b : Int) {
println("Result of $a + $b is ${a+b}")
}

fun
main (args: Array<String>){
add(4,5)
}

In a function declaration, variables cannot be declared with the val or var keywords, and the data type must be specified explicitly.

Functions with parameters and return types

Functions can receive parameters and return values as a result. See the following example, which takes a value as a parameter and returns the result:

fun myFun(message : String) : String {
return "Hello from $message"
}

fun main (args: Array<String>){
val result = myFun("Author")
println(result)
}

This function takes a string as a parameter and returns a string value. Like other programming languages, Kotlin also uses the return keyword to return a value from a function. The return value must be the same as the return type defined in the function signature:

fun add(i: Int, j: Int): Int

If a return type of the function is integer, the return statement must be an integer, otherwise the compiler will throw an error. Here is an example of a function called addValues. This takes two integer parameters, adds them, and returns an integer as a result:

fun addValues(i: Int, j: Int): Int{
val k = i + j
return k
}

fun main (args: Array<String>) {
val result = addValues(5,6)
println(result)
}

Function as an expression

In Kotlin, a function can behave as an expression. Let's take the example of the add function from the previous section and convert it into an expression:

fun addValues(i: Int, j: Int): Int {
return i + j
}

This function takes two parameters, adds them, and returns an integer value. When the function contains only one line of code, it can be written as an expression.

Create a new function as an expression:

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

The explicit declaration of the function return type can be removed as follows:

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

Now add an equals operator, remove the curly brackets, and remove the explicit type declaration of the return type along with the return keyword from the function body. The compiler will figure out the return type by itself. Next, call addValues and addValuesEx in our main. Verify the output of each function:

fun addValues(i: Int, j: Int): Int{
return i + j
}

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

fun main (args: Array<String>) {
var result = addValues(5,6)
println(result)

result = addValuesEx(5,6)
println(result)
}

Take another example. Write a function that takes two integer variables and returns the highest one. Return any value if both values are same:

fun getMaxEx(x: Int, y: Int) =
if(x >= y){
x
} else {
y
}

fun main (args: Array<String>) {
var val1 = 8
var val2 = 6

max = getMaxEx(val1,val2)
println("$val1 , $val2 : Max value is $max")
}

Writing a function as an expression always helps to remove unwanted code, but sometimes this convenience can be problematic. Add minor changes in the following function by adding a string value in if and else statements and executing it:

 fun getMaxExx(x: Int, y: Int) =
if(x >= y){
x
"Scary"
} else {
y
"Yes it is"
}

fun main (args: Array<String>) {
var val1 = 8
var val2 = 6
var maxEx = getMaxExx(val1,val2)
println("$val1 , $val2 : Max value is $maxEx")
}

This function returns Scary as output. When a function is written as an expression and no return type is defined in the function signature, the type inference comes into action, which means that Kotlin always returns the last line of code and the compiler evaluates the return type at runtime. If the line is an integer or a string, it will be decided accordingly. In this example, the expected output is an integer, but a string is returned. This tricky situation can be overcome by declaring the function return type:

fun getMaxEx(x: Int, y: Int) : Int =
if(x >= y){
x
"Scary"
} else {
y
"Yes it is"
}

If the return value is anything other than declared type, the compiler will throw the following error:

Type mismatch: inferred type is String but Int was expected

Functions with default arguments

Kotlin makes it possible to assign a value to a parameter in the function declaration. If the function is invoked without passing a value, then the compiler automatically assigns a default value to it. The hello function prints Hello Kotlin if no value is passed to the function:

fun hello(message : String = "Kotlin") : Unit{
println("Hello $message")
}

fun main (args: Array<String>) { hello()
hello("World")
}

The default argument is a very helpful feature in many situations. For example, consider a situation in which we are writing a currency exchange function that converts dollars into another currency and applies service charges on the conversion:

fun currencyExchange(dollar: Double, currencyRate: Double, charges: Double): Double {
var total = dollar * currencyRate
var fees = total * charges / 100
total = total - fees
return total
}

Let's assume that we want to convert 100 US dollars to Swedish krona. 1 dollar is equal to 10 Swedish krona, and our company charges 5% on the total amount:

fun main (args: Array<String>) {
var total = currencyExchange(100.0,10.0, 5.0)
println(total)
}

This function works fine; it multiplies the dollar by the target currency, calculates the charges, and returns the total amount after deductions. In the currency market, currency prices move quite rapidly, so it is a good idea to check the currency rate before conversion. However, its highly likely that conversion charges (5% in this example) will remain the same for a long period of time. If this is true, then the default value can be assigned to the charges variable, as follows:

fun currencyExchange(dollar: Double, currencyRate: Double, charges: Double = 5.0): Double {
var total = dollar * currencyRate
var fees = total * charges / 100
total = total - fees
return total
}

By setting the default value, the function can be invoked without a third parameter:

fun main (args: Array<String>) {
var total = currencyExchange(100.0,10.0)
println(total)

var
total = currencyExchange(100.0,10.0, 3.0)
println(total)
}

Functions with named parameters

Kotlin makes it possible to specify the argument's name in a function call. This approach makes the function call more readable and reduces the chance to pass the wrong value to the variable, especially when all variables have the same data type. To understand the importance of this feature, let's take the previous example of currency conversion:

fun currencyExchange(dollar: Double, currencyRate: Double, charges: Double = 5.0): Double {
var total = dollar * currencyRate
var fees = total * charges / 100
total = total - fees
return total
}

The currencyExchange function takes three parameters of the Double type—dollar, target currency, and conversion charges:

fun main (args: Array<String>) {
var total = currencyExchange(100.0, 6.0, 10.0)
println(total)
}

The currencyExchange function will perform the currency conversion and return the result. If the function contains a long list of variables as a parameter, then there is a high chance that values can be passed in the wrong order. In this example, the currency rate is swapped with conversion charges:

var total = currencyExchange(100.0, 6.0, 10.0)
println(total)
Output is 540 instead of 940 Swedish crown.

The program will execute without any errors because arguments passed to the function are the correct types but are in the wrong order. To solve this problem, Kotlin provides a feature called named parameters. Named parameters make it possible to pass different values to the function by explicitly defining a parameter's name. Using the argument's name helps to pass the correct value to each argument and makes the code clean and readable:

total = currencyExchange(dollar = 100.0, currencyRate = 10.0, charges = 6.0)
println(total)

By mentioning the name of each argument, function arguments can pass in any order:

fun main (args: Array<String>) {
var total = currencyExchange(dollar = 100.0, currencyRate = 10.0, charges = 6.0)
println(total)
total = currencyExchange(currencyRate = 10.0, charges = 6.0, dollar = 100.0)
println(total)
}

Functions and vararg

Kotlin allows programmers to pass arguments separated by commas to the function. These arguments are automatically converted into an array. This is called a vararg, a variable argument. Declare a vararg along with its type in the function declaration:

fun varargString(vararg list : String){
for (item in list){
println(item)
}
}

fun main (args: Array<String>) {
varargString("ett","tva","tre")
varargString("Sat","Sun","Mon")
}

As another example, write a function that takes an integer vararg as a parameter, adds it, and displays the total on the screen:

fun addVararg(vararg list: Int){
var total = 0
for (item in list){
total += item
}
println("Total $total")
}

fun main (args: Array<String>) {
addVararg(1,2,3,4,5,6,7,8,9,10)
}

vararg makes a programmer's life easier, especially when he or she is not sure about how many parameters may be required for one function:

fun add(a: Int, b: Int , c: Int , d: Int, e: Int)
fun add(vararg list : Int)

The first add function is restricted to the limited amount of variables declared in the function signature, but the add function with vararg can operate on all comma-separated values that we will pass to it.

vararg with other arguments

There is a possibility that we will be asked to create a function with an integer vararg along with two integer variables. See the following example:

fun trickyVararg(vararg list: Int, a : Int, b: Int){
var total = 0
for (item in list){
total += item
}

println("Total $total")
println("a = $a , b = $b")
}

fun main (args: Array<String>) {
trickyVararg(1,2,3,4,5)
}

The first three values (1,2,3) are for vararg list; parameter a is assigned with value 4 and parameter b is assigned with value 5. However, the compiler will throw the following errors:

Kotlin: No value passed for parameter 'a'
Kotlin: No value passed for parameter 'b'

vararg list is declared first in the function signature, and the compiler considers the all input for vararg list. This problem can be solved by declaring vararg as the last function argument:

fun trickyVararg(a : Int, b: Int, vararg list: Int){

var total = 0
for (item in list){
total += item
}
println("Total $total")
println("a = $a , b = $b")
}
fun main (args: Array<String>) {
trickyVararg(4,5,1,2,3)
}

The compiler will now assign the first two values to the a and b variables, and the rest will be assigned to vararg list. Declaring vararg at the end of the function signature is good practice, but it is not necessary. It can be declared at the beginning, but we must ensure that we call the rest of the variables by name:

fun trickyVararg02(vararg list: Int, a : Int, b: Int){
var total = 0
for (item in list){
total += item
}
println("Total $total")
println("a = $a , b = $b")
}

fun main (args: Array<String>) {
trickyVararg02(1,2,3,a=4, b=5)
}

Notice that by declaring the parameters' names in the function call, the compiler does not complain about missing values.

Package-level functions

Programmers with a Java background are familiar with static methods. Static methods are declared within a class and can be accessed directly by using class names as a reference. Kotlin does not have a static method, but it does provide a package-level function instead. To create a package-level function, do the following:

  1. Create a package.
  2. Create a file in the package.
  3. Create a function in the file.
  4. In the project explorer, select a folder where we want to add a package and then right-click New followed by Package.
  5. In the newly opened window, add a package name, for example, the Util package.
  6. Once the Util package (folder) is created, right-click on package and add the Kotlin file. Call this file MyUtil. Our Kotlin file is now ready to have package-level functions added to it.

Now open the MyUtil.kt file. We should see the following line here:

package Util

A directory or folder is called a package, which is where the Kotlin file resides. The package name is used as a reference to access the function. Let's create a function with a greeting message as follows:

package Util
fun hello() = println("Hello from Package Util")

The package-level function hello() has now been created and can be accessed by using the package name as follows:

PackageName.FunctionName()

Let's create the MyUtil package and add the MyTestUtil.kt file:

  1. Now open the MyTestUtil.kt file, add a main function, and call a package-level function, hello, to the file. A package-level function is always called using the package name as a reference:
fun main(args: Array<String>) {
Util.hello()
}
  1. Execute the program and verify the output, Hello from Package Util. Add different functions under the Util package as follows:
fun hello() = println("Hello from Package Util")

val PI = 3.1415926535 // Package level variable

// Calculate power of given number
fun myPow(base : Double, exp: Double) : Double {
var result = 1.0
var counter = exp
while (counter > 0) {
result*= base
counter--
}
return result
}

// Calculate area of a circle
fun areaOfCircle(radius : Double) : Double{
return PI * 2 * radius
}

// Generate random number within given range
fun myRandom(range: IntRange) : Int{
return range.shuffled().last()
}
  1. Call these functions in the main function and verify the result as follows:
fun main(args: Array<String>) {

Util.hello()

println("Power Function")
println(Util.myPow(5.0,3.0))

println("Random number generator")
var range = 1..50
for (i in 1..5) {
println(Util.myRandom(range))
}

println("value of PI is ${Util.PI}" )
println("Area of circle " + Util.areaOfCircle(4.0))
}

How to access a function

There are a couple of ways to access a package-level function. One way to do this is by using a package name with each function. This method has already been used in the previous example:

Util.hello()

The second way to access a package-level function is to import each function explicitly by using the import keyword:

import Util.hello
fun main(args: Array<String>) {
hello()
println("Power Function")
println(Util.myPow(5.0,3.0))
}

Notice that by adding import Util.hello, Kotlin allows us to use the hello function without using a package name with it. The third and most common way to import a package is with the wildcard:

import Util.*
fun main(args: Array<String>) {
hello()
println("Power Function")
println(myPow(5.0,3.0))
println("Random number generator")

var range = 5..50
for (i in 1..5) {
println(myRandom(range))
}

println("value of PI is ${PI}" )
println("Area of circle " + areaOfCircle(4.0))
}

This is the most convenient method. By using this approach, functions can be accessed by using a package name.

 

Summary

In this chapter, we have learned about the different types of variables and their declarations. We also discussed type inference and how Kotlin helps to improve productivity by allowing a programmer to ignore explicit type declarations. This chapter has explained null safety and how Kotlin helps us to write safe and concise code. We have also discussed if statements, else as an expression, loops, labeled for loops, continue, and break statements in detail. This chapter concluded by discussing functions and their important features, including functions as an expression, named parameters, and package-level functions.

 

Questions

  1. Why is Kotlin's popularity growing?
  2. What is type inference in variable declaration?
  3. What is null safety and why is it important?
  4. What are named parameters and why they are important in function calls?
  5. What is a package-level function?
  6. What is a labeled for loop?
 

Further reading

About the Authors
  • Abid Khan

    Abid Khan is an application developer and test engineer with over 10 years of experience. He has worked with different programming languages, including C/C++ and Java, and is now working with Kotlin as a primary language for Android development. Abid is also the author of a book, Hands-On Object-Oriented Programming in Kotlin. He lives in Stockholm, Sweden, and spends most of his time reading books, learning new technologies, and blogging.

    Browse publications by this author
  • Igor Kucherenko

    Igor Kucherenko is an Android developer at Techery, a software development company that uses Kotlin as the main language for Android development. Currently, he lives in Ukraine, where he is a speaker in the Kotlin Dnipro community, which promotes Kotlin, and shares his knowledge with audiences at meetups. You can find his articles concerning Kotlin and Android development on Medium and in a blog for Yalantis, where he worked previously.

    Browse publications by this author
Hands-On Object-Oriented Programming with Kotlin
Unlock this book and the full library FREE for 7 days
Start now