π Kotlin language features
Kotlin is an object oriented language with functional features. This chapter covers important and relevant features of the language slit into basic and intermediate. Another chapter covers advanced features.
Basic features
Basic constructs (variables, control flow)
- Kotlin is statically typed and supports implicit typing.
- Static typing: types cannot change on runtime (it is the opposite of dynamic typing).
- Implicit typing: the compiler can infer the type whenever possible.
var
creates mutable variables.val
creates immutable variables or constants.- Semi-colons are optional.
- Kotlin supports top level declaration of variables and functions (They can be declared outside of classes).
- String interpolation is available with this syntax
${expression}
. if
, andwhen
statements are expressions (they can return a value).when
is equivalent to theswitch
statement of other languages- The ternary operator is not available but the
if
statement replaces-it.
- for-each is the only type of for loop available.
- Object oriented programming is supported as in Java with some additional features such as extensions.
- The compiler supports Null Safety. It allows to write code free from null pointer errors with a compile time guarantee.
- Functional programming is supported (Higher-order functions and functions as 1st class items, etc.).
Use val by default
Replace by var
only if needed.
βΆοΈ this code highlights the above features.
Functions
In the this section, the terms 'argument' and 'parameter' are used interchangeably.
Functions in Kotlin have the following features:
- Declaration:
fun functionName(arg1: type1 = defaultvalue1, ...) : retunrType
. - Call a function by passing the value in the declaration order.
- Use argument labels for more clarity, however, it also allows for arbitrary ordering of arguments.
- Optional arguments have a default value and can be omitted during the call.
- Functions are first class items or citizen: they can be assigned to a variable, passed as a function parameter, or returned from a function.
- π‘ A function that takes a function as an argument or returns one is a higher order function.
- A function type can be expressed as follows:
(typeOfParam1, typeOfParam2, etc) -> returnType
(The empty return type isUnit
). - Anonymous functions use the following syntax
{ argName1, argName2, etc. -> // code }
- Also called or lambda functions or literal functions
- The last function argument can be put after the closing after the closing parenthesis
compute(9, 5) { x, y -> x * y }
βΆοΈ this code highlights the above features.
The next section talk about null safety.
Null safety
In a nutshell, null safety is a compiler feature that eliminates the infamous Null pointer exception or npe. The Kotlin compiler reports errors and warnings when we manipulate nullable (also called optional) values. Here is a list of null safety features provided by Kotlin:
- All types are non-nullable by default; we cannot assign
null
to a variable or an argument.- For example, this code fails
var s: String = null
.
- For example, this code fails
- A type can be made nullable by suffixing it with a ?. For example:
var s: String? = null
. - Kotlin forbids calling a method or a property of a non-nullable type, unless we do one of these possibilities:
- Use optional chaining with the ? suffix.
- Provide a default value with the elvis ?: operator.
- Smart-cast the nullable into a non-nullable.
- Use the !! operator that eliminates compiler checks. This should never be used.
Never unwrap with !!
Use other safe techniques instead.
βΆοΈ this code illustrates null safety and how to use optional types.
Java `Optional` does not provide compile time null checks
Optional
wrap null values on runtime. The Java compiler (as of version 17) does not provide unwrapping features such as smart casting. It is still possible to have a npe like this: Optional<String> s = null; s.isPresent()
;
Enumerations
Enumerations allow to work with a group of values in a type-safe fashion. Unlike Java enums, Kotlin enums are classes. Kotlin enum classes provide these features:
when
statements support enumerations.- Enum constants can declare their own anonymous classes with their corresponding methods, as well as with overriding base methods.
- An enum class can implement an interface but it cannot derive from a class
- There are methods for listing the defined enum constants and getting an enum constant by its name.
- Every enum constant has properties for obtaining its name and position (starting with 0).
βΆοΈ this code illustrated the features above. For further reading please consult the official documentation.
π§ͺ Exercises
Exercise 1
Please click on this link to view the exercise
Please open to see the solution(s)
Exercise 2
Please click on this link to view the exercise
Please open to see the solution(s)
Intermediate features
Object oriented programming
Kotlin allows to write concise OOP code and has the following features:
- Available common features: classes, inheritance, interfaces, and abstract classes.
- Native support of properties: do not define getters and setters unless needed.
- Just add
get()
andset(value)
functions next to the property declaration.
- Just add
- Constructor arguments are defined next to the class name
class ClassName(arg1, atg2, )
- Prefixing a constructor arguments with
val
orvar
makes it a property (val
makes it read-only). - The constructor name is
init
and does not require parameters. - The compiler checks that all non-nullable properties are initialized by the end of the constructor.
- β οΈ The compiler does not check the initialization of
lateinit
properties. Thus, accessing them before while uninitialized causes an exception.
- β οΈ The compiler does not check the initialization of
- Prefix classes with
open
to allow inheritance. - Kotlin enables the
public
access level by default. - The equality operator
==
callsequals()
(as opposed to Java which uses reference equality). - A companion object contains static methods and properties.
- Extensions add function and properties to existing classes.
- π‘ They replace inheritance in many situations.
- For example, we can add functions to the String class instead of creating a new
StringUtils
class.
- Sealed classes and interfaces cannot be extended or implemented by third parties.
Do not define accessors unless needed
As opposed to Java, Kotlin supports properties and allows to add accessors later without refactoring the code that calls these properties. Thus, by default, just define the name of properties without accessors and use them directly.
βΆοΈ this code illustrates some features.
Data class
Data classes are final (cannot be inherited from) classes that provide standard functionality:
equals()
andhashCode()
toString()
of the form"class(field=value, ...)"
componentN()
that correspond to the properties in their order of declaration.copy()
However, they have the following constraints:
- The primary constructor needs to have at least one parameter.
- All primary constructor parameters need to be marked as val or var.
- They cannot be abstract, open, sealed, or inner (π‘ but extensions are possible).
βΆοΈ this code illustrates some features.
Functional programming
General concepts
Functional programming revolves around these concepts: pure functions, recursion, referential transparency, immutable variables, functions as first-class citizens, and higher-order functions.
Let's briefly explain these concepts:
- Immutable variables means that we cannot change the value of a variable or its properties once it has been created. If we want to do so, we must create a new instance with the new value.
- Pure functions are functions that do not have side effects and will thus return always the same output given the same input.
- Functions are first class citizens: they can be assigned to a variable or used in higher-order functions (passed as a function parameter to another function or returned from a function).
- Referential transparency: means that an expression can be replaced by its result without changing the behavior of the program. Transparency refers to the fact that the implementation of the expression is irrelevant.
π‘ Pure functional languages provide these features natively and enforces them (at build time).
Kotlin and functional programming
Kotlin is not a pure functional languages but it supports some features. For example, Kotlin does not have compile time verification of pure functions, but it provides immutable collections through the kotlinx.collections.immutable library.
listOf generates read-only lists, which are not immutable
A read-only list cannot add or remove elements, but it can change the underlying data.
@Test
fun givenReadOnlyList_whenCastToMutableList_checkNewElementsAdded(){
val list: List<String> = listOf("This", "Is", "Totally", "Immutable")
(list as MutableList<String>)[2] = "Not"
assertEquals(listOf("This", "Is", "Not", "Immutable"), list)
}
The Arrow-kt library add more functional programming features.
Declarative programming
Declarative programming is a famous style within functional programming. It consists of writing code as a chaining of function calls in this style val result = f(x).g(y). ...
. Higher order functions replace many situation where we would use loops. This favors readable code which is easy to debug an maintain.
βΆοΈ this code show an example of list manipulation using declarative programming.
Kotlin and Java interoperability
- Kotlin is designed with Java interoperability in mind.
- Kotlin code may require some annotations to be called from Java.
- It is possible to mix Java and Kotlin in the same project.
- JetBrain's IntelliJ and Android Studio can convert to Kotlin when pasting java code.
- Kotlin generates Java records by annotating a data class with
@JvmRecord
and targeting JVM 16, among other requirement listed here. - It is much more easier and natural to call Java from Kotlin.
- For example: Java accessors are converted to Kotlin properties.
βΆοΈ this code shows how to convert a Kotlin List
to a Java ArrayList
.
The official documentation provides exhaustive documentation on Kotlin and JVM integration
π§ͺ Exercises
Exercise 3
Please click on this link to view the exercise
Please open to see the solution(s)
Exercise 4
Please click on this link to view the exercise
Please open to see the solution(s)
Exercise 5
Please click on this link to view the exercise
Please open to see the solution(s)
Exercise 6
Please click on this link to view the exercise
Please open to see the solution(s)
Exercise 7
Please click on this link to view the exercise