Skip to content
Application Development II
GitLabGitHub

Generics and Extensions

Final Kotlin concepts we need to learn in this class.
Overview

These notes are, in part, adapted from the Kotlinlang.org docs on Generics and Extensions: Functions.

There are two last very important concepts to learn in Kotlin which will help realize the full potential of the Jetpack Compose libary.

One is common to other languages, is very important for working with Collections, and you may have seen it before: Generics.

The other is unique to Kotlin and is a very neat technique that replaces the Decorator pattern: Extensions.

Classes in Kotlin can have type parameters, just like in Java:

class Box<T>(t: T) {
var value = t
}
class Box<T>(t: T) {
var value = t
}

To create an instance of such a class, simply provide the type arguments:

val box: Box<Int> = Box<Int>(1)
val box: Box<Int> = Box<Int>(1)

But if the parameters can be inferred, for example, from the constructor arguments, you can omit the type arguments:

val box = Box(1) // 1 has type Int, so the compiler figures out that it is Box<Int>
val box = Box(1) // 1 has type Int, so the compiler figures out that it is Box<Int>

Classes aren’t the only declarations that can have type parameters. Functions can, too. Type parameters are placed before the name of the function:

fun <T> singletonList(item: T): List<T> {
// ...
}
fun <T> T.basicToString(): String { // extension function
// ...
}
fun <T> singletonList(item: T): List<T> {
// ...
}
fun <T> T.basicToString(): String { // extension function
// ...
}

To call a generic function, specify the type arguments at the call site after the name of the function:

val l = singletonList<Int>(1)
val l = singletonList<Int>(1)

Type arguments can be omitted if they can be inferred from the context, so the following example works as well:

val l = singletonList(1)
val l = singletonList(1)

You can read more detail about the theory and usecases for generics at the Generics documentation on Kotlinlang.org

I will include the following exercises in assignment 4 to help you get the hang of using generics in Kotlin. More details to come soon, but here are the exercises for now:

Kotlin provides the ability to extend a class or an interface with new functionality without having to inherit from the class or use design patterns such as Decorator. This is done via special declarations called extensions.

For example, you can write new functions for a class or an interface from a third-party library that you can’t modify. Such functions can be called in the usual way, as if they were methods of the original class. This mechanism is called an extension function. There are also extension properties that let you define new properties for existing classes.

To declare an extension function, prefix its name with a receiver type, which refers to the type being extended. The following adds a swap function to MutableList<Int>:

fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // 'this' corresponds to the list
this[index1] = this[index2]
this[index2] = tmp
}
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // 'this' corresponds to the list
this[index1] = this[index2]
this[index2] = tmp
}

The this keyword inside an extension function corresponds to the receiver object (the one that is passed before the dot). Now, you can call such a function on any MutableList<Int>

This function makes sense for any MutableList<T>, and you can make it generic:

val list = mutableListOf(1, 2, 3)
list.swap(0, 2) // 'this' inside 'swap()' will hold the value of 'list'
val list = mutableListOf(1, 2, 3)
list.swap(0, 2) // 'this' inside 'swap()' will hold the value of 'list'
fun <T> MutableList<T>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // 'this' corresponds to the list
this[index1] = this[index2]
this[index2] = tmp
}
fun <T> MutableList<T>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // 'this' corresponds to the list
this[index1] = this[index2]
this[index2] = tmp
}

You need to declare the generic type parameter before the function name to make it available in the receiver type expression.

I will include the following exercises in assignment 4 to help you get the hang of using Extensions in Kotlin. More details to come soon, but here are the exercises for now: