Basics of Kotlin Language
What is Kotlin?
Kotlin is an open-source, statically typed programming language. Kotlin is developed by JetBrains, the company behind the IntelliJ IDE, and other development tools. They designed Kotlin to be 100% interoperable with Java, meaning that you can use any Java libraries and frameworks in Kotlin, and vice versa
As previously mentioned, you can use Kotlin on any platform. Currently, it’s most prominent for Android app development.
Who’s Using Kotlin?
By now, Kotlin is used by too many large companies to name them all. But just to convince you that Kotlin is used in production by some of the largest tech companies in the world, here’s a selection of them:
- Netflix
- Uber
- Slack
- Udacity
- Evernote
- Trello
- Slack
- Lyft
Many of these companies use Kotlin on Android, but also on server-side and other platforms.
Why Learn Kotlin?
there are many companies actively looking for Kotlin developers, especially in the Android development space. So having experience in Kotlin can give you an edge in the interview process.
But even without the goal of landing a Kotlin developer job, learning the language and its concepts will allow you to quickly master other modern languages such as TypeScript, Scala, and Swift because they share many language concepts.
Principles and Goals
Kotlin aims to be a readable, pragmatic, safe, and interoperable programming language:
- Readability is supported by language features such as type inference, data classes, and infix functions. Such features allow writing concise code without losing readability.
- Pragmatism is crucial because Kotlin is for large-scale enterprise software development. JetBrains use it themselves to develop their IDEs. Thus, Kotlin incorporates industry feedback and addresses issues of large-scale software development.
- Safety aims to prevent common software bugs by design. This is aided by several language features such as nullable types (to prevent null pointer exceptions) and by nudging you towards best practices such as designing for inheritance.
- Interoperability with Java is a major selling point of Kotlin and a necessary base for its widespread adoption in the JVM world. Interoperability allows Kotlin and Java to be used side by side, including the use of Java libraries or frameworks from Kotlin. For instance, the Kotlin standard library interoperates with Java by reusing the Java Collections API. Similarly, it interoperates with JavaScript in the context of Kotlin/JS.
Variables and Data Types
Read-Only vs Mutable Variables
Learn the fundamental principle of read-only and mutable variables, plus how and when to use them in Kotlin.
Mutable Variables
To declare a mutable variable, you use the var
keyword:
Mutable means that the variable can be reassigned to a different value after the initial assignment.
Read-Only Variables
In contrast, a read-only variable can be declared using val
(instead of var
):
Read-only means that the variable cannot be reassigned once initialized.
You should prefer read-only variables to mutable ones whenever possible, i.e., whenever you don’t have to change the value after initialization.
Tip: Prefer
val
tovar
to simplify data flow and facilitate reasoning about your code.
Basic Data Types
Kotlin is a statically typed language, meaning that the data type of every expression is known at compile time.
Integers
There are four basic data types to store integer numbers of different sizes in Kotlin:
Floating Point Numbers
Additionally, Kotlin has Float
and Double
to store floating-point numbers up to different precision and sizes:
Two things to note:
- The
e
in both values denotes exponentiation, for instance10e3 == 1000
. - In order to denote a
Float
value, you have to add thef
suffix. Otherwise, Kotlin infersDouble
as the type of number.
Text
Kotlin uses the Char
type for single characters and String
for arbitrary sequences of characters:
However, you can also use multiline strings by wrapping your string into three double quotes: """<multiline string here>"""
Booleans
Finally, Kotlin uses Boolean
to store either true
or false
:
In contrast to Java, Kotlin has no primitive types. All types discussed in this lesson are objects at runtime
Nullable Types
Thus, all objects and types you’ve seen so far in this course were non-nullable. Trying to assign null
to them would cause a compile-time error:
Declaring Nullable Types
Let’s see how you can use nullable types when necessary.
The nullable counterpart of some type A
is denoted as A?
. For example, String?
denotes a nullable string and Int?
denotes a nullable integer.
If you’re familiar with union types (e.g. from TypeScript), think of these as
String? = String | null
. In other words, aString?
is either aString
ornull
.
Assigning null
to a nullable type works as you’d expect:
Safe Call Operator
To access members of a nullable object in Kotlin, you use the safe call operator by appending a ?
to the object:
The safe call operator works as follows:
- If the object is
null
, it evaluates tonull
. - Otherwise, it dereferences the objects and evaluates to the value of the overall expression.
Elvis Operator
When working with nullable data, you often want to define default values that should be used in case an object is null
.
In Kotlin, this is done using ?:
, the so-called Elvis operator:
You can read the Elvis operator as “or else”. Here, chatName
is equal to name
or else "Anonymous"
.
This operator is useful to get rid of nullability in your code. It’s good practice to replace null
values with useful default values as early as possible, as this will simplify calling code.
Conditions
For conditional control flow, Kotlin uses the if
and when
keywords. The syntax of if
-blocks are the same as in C and Java. Similarly, the when
the keyword is similar to switch
in these languages but more powerful, as you will learn.
Conditions using if
Conditions with if
can be written as follows:
Equality and Comparison Operators
Kotlin’s operators to formulate conditions are the following:
Logical Operators
To build up more complex conditions from primitive conditions (using the operators above), Kotlin provides the standard logical operators:
Conditions Using when
You can use when
conditions to define different behaviors for a given set of distinct values. This is typically more concise than writing cascades of if
-else if
conditions. For instance, you can replace the if
condition above with a when
condition to save around 30% of the lines of code
The when
the keyword is followed by the variable it compares against. Then, each line defines the value to check on the left-hand side, followed by an arrow ->
and the block of code to execute if the value matches.
The code block on the right-hand side must be wrapped in curly braces unless it’s a single expression. Optionally, an else
-block can be used to define a block of code to run if no other case matches.
This language construct is basically the same as switch
from languages like C or Java. However, it’s more powerful since it supports more complex checks:
As you can see, there are various ways to define the value(s) to compare against:
- Fixed value (
700
) - Multiple fixed values (
0, 1, 2
) - Ranges (
in 300..699
) - Ranges with negation (“not in range”) (
!in 0..300
) - Function call (
earthSurfaceTemp()
) - Type check (
is Int
) - Default case (
else
)
Good Practice: if
vs when
From a theoretical viewpoint, if
and when
are equally powerful. Coming from other languages, using if
will feel more natural in most cases. Using when
is definitely preferable when comparing two or more distinct fixed values.
However, in aiming to write idiomatic Kotlin code, I’d encourage you to consider the when
construct in more cases than the switch
construct known from other languages. In many cases, a when
the condition will be a more concise and readable alternative to a regular if
condition, especially whenever the right-hand sides are single expressions.
In Kotlin, both if
and when
can be used as expressions instead of statements. An expression is a piece of code that has a value, e.g. "Kotlin"
, 42 * 17
, or readInput()
. In contrast, a statement is a piece of code with no value, such as fun foo() { ... }
or while (active) { ... }
. In many programming languages, if
and when
/switch
are statements. But in Kotlin, they are expressions!
Collections
Collections are a central feature of any programming language. They allow storing multiple values — such as your list of favorite books — in one convenient data structure which can then be stored and accessed with a single variable.
The fundamental collection types you’ll master in the following lessons are:
- Lists
- Sets
- Arrays*
- Maps
- Arrays are not part of the actual Collections API but are included here because they’re also a data structure for storing multiple elements.
Creating a List
Kotlin provides three convenient functions to initialize lists:
All three functions allow you to add any number of elements (try it out!). The main difference between them is that listOf
creates a read-only list whereas mutableListOf
creates a mutable list. What does this mean? With read-only lists, you cannot add or remove elements from the list after it’s created. With a mutable list, however, you can.
Read-only vs Mutable Lists
The differentiation between read-only lists and mutable lists has the same underlying intention as differentiating between val
vs var
: immutability. At this point, it’s important to understand how val
and read-only data structures differ. They essentially represent two levels of immutability:
val
prevents an initialized variable from being reassigned to another value. In other words, the memory address the variable points at cannot be changed; it will always point to the same memory address.- Read-only collections prevent adding or removing elements from an existing collection but don’t affect any variables that may point to these collections. In other words, they disallow changing values in the section of the computer’s memory that stores the collection.
Sets are linear data structures very similar to lists. However, they do have a few notable differences.
What is a Set?
Just like a list, a set is a linear data structure that contains multiple elements. However, sets are different from lists because:
- Sets have no concept of order, meaning there is no such thing as “the third element” in a set.
- Sets cannot contain duplicates; you cannot have a set that, for instance, contains
42
twice.
Both these properties of sets have implications for programming with them that you should be aware of when using them.
Creating a Set
The way to create a set should look familiar to you:
- Sets cannot have duplicates, so adding an existing element has no effect
- Sets have no concept of order, so you can’t access an element by index
- You can create sets using
setOf
andmutableSetOf
What is an Array?
An array is a basic data structure in all programming languages. It stores multiple elements by placing them consecutively in memory.
What’s the Difference Between Arrays, Lists, and Sets?
Sets are easy to distinguish from both lists and arrays based on their special properties: no ordering and no duplicates.
To understand the difference between lists and arrays, we’ll have to look at the way their elements are stored and what implications this has for us as developers.
While arrays store their values in one consecutive part of computer memory, list and set elements may be spread across the memory. This has several implications:
- Arrays are a fixed-length data structure. This is because, after the array is initially created, the next consecutive place in memory may be taken by another value. Thus, no more consecutive values can be added to the array. Not being able to add or remove elements distinguishes them from mutable lists.
- Differences between arrays and read-only lists exist only on a lower level, i.e., in the exact API they provide, the storage structure in memory, and their resulting runtime performance.
Finally, there’s no differentiation between read-only and mutable arrays in Kotlin. Array elements can be overwritten but no elements can be added or removed, as they’re fixed in length.
A map is a collection of keys associated with values. This is a concept you may also know as dictionaries (e.g., Python) or associative arrays (e.g., PHP). Each key is associated with one unique value. This value can have any type. In particular, it may be a collection, such as a list or a nested map.
Creating a Map
Once again, Kotlin provides a convenient way to initialize (read-only) maps:
Creating a Mutable Map
As you may expect by now, Kotlin offers mutableMapOf
to create a mutable map:
By using a mutable map, you can add and remove entries from the map,
- Basic maps are created using
mapOf
ormutableMapOf
. - Again, prefer read-only maps over mutable ones.
- Kotlin provides
to
for creating map entries in a readable way (myKey to myValue
). - You can add entries to a mutable map using
myMap[key] = value
. - Overwriting an existing entry works the same way (if
key
already exists). - You can remove entries from a mutable map using
myMap.remove(key)
.
Loops
Kotlin offers the three standard types of loops:
while
loopsdo-while
loopsfor
loops
As a programmer, loops in Kotlin will feel familiar quickly. Let’s look at a few examples, starting with while
and do-while
loops.
while
Loops
Kotlin’s while
loops have exactly the same syntax and semantics as while
loops in Java or C:
The while
the keyword is followed by a condition. The loop will repeat its code block until this condition evaluates to false
.
This code example approximates the square root of the input
up to an error margin of at most 0.001
.
do-while
Loops
The do-while
loop checks its condition after each iteration, whereas the regular while
loop checks it before each iteration. Consequently, a do-while
loop always performs at least one iteration. This is why it’s typically used in cases where that first iteration gets some initial data, e.g. user input:
The advantage here is that you can limit the scope of the input
variable to the loop block, which is not possible using a regular while
loop. It’s good practice to minimize the scopes of your variables.
In contrast to while
loops, Kotlin’s for
loops work a bit differently than you may be used to from other languages.
for
Loops
Kotlin’s for
loops are different from many other languages because they’re used in the same manner as for-each loops (or for-in loops). Thus, you always give an iterable to the for
loop over which it iterates:
for
Loops vs while
Loops
As with if
and when
, you have multiple language constructs for one concept: repeating blocks of code. So when would you prefer for
over while
and vice versa?
while
loops are used when the number of iterations is not known in advance.- Consider again the approximation algorithm above. It may take any number of iterations to get below the required error margin because it’s not a fixed number.
for
loops are superior when the number of iterations is known in advance.- In other words, the number of iterations is fixed because it’s equal to the length of the given iterable.
content reference from educative.io