Menu
Coddy logo textTech

Generic Functions

Part of the Object Oriented Programming section of Coddy's Kotlin journey. Lesson 37 of 57.

A generic function works with any type. Its type parameter, written in angle brackets before the function name, stands for the type the caller uses. T is the usual name:

fun <T> firstOr(list: List<T>, default: T): T = if (list.isEmpty()) default else list[0]

Inside main:

println(firstOr(listOf(3, 5), 0) + 1)
println(firstOr(listOf<String>(), "none").uppercase())

Output:

4
NONE

Kotlin infers the type argument from the arguments. When there is nothing to infer it from, you write it yourself, as in listOf<String>() above:

fun <T> repeatItem(item: T, times: Int): List<T> = List(times) { item }

Inside main:

println(repeatItem("ab", 3))
println(repeatItem(7, 2))
val empty = repeatItem<Double>(1.5, 0)
println(empty)

Output:

[ab, ab, ab]
[7, 7]
[]

A function can have several type parameters, and a function-type parameter can use them. This is how library functions such as map work for every element and result type:

fun <A, B> transform(items: List<A>, f: (A) -> B): List<B> {
    val out = mutableListOf<B>()
    for (item in items) out.add(f(item))
    return out
}

fun <K, V> invert(map: Map<K, V>): Map<V, K> = map.entries.associate { (k, v) -> v to k }

Inside main:

println(transform(listOf("kiwi", "fig")) { it.length })
println(invert(mapOf("a" to 1, "b" to 2)))

Output:

[4, 3]
{1=a, 2=b}

A type parameter keeps the caller's type. firstOr(listOf(3, 5), 0) returns an Int, so + 1 compiles. A version written for List<Any> would accept every list too, but it would return Any and force a cast at every call:

fun firstAny(list: List<Any>, default: Any): Any = if (list.isEmpty()) default else list[0]
// firstAny(listOf(3, 5), 0) + 1               // error: Any has no plus
(firstAny(listOf(3, 5), 0) as Int) + 1         // compiles, but needs a cast
firstOr(listOf(3, 5), 0) + 1                   // generic: the result is an Int
challenge icon

Challenge

Easy

Write two generic functions that work for lists of any type. countWhere(items, predicate) returns how many elements pass the predicate, a function (T) -> Boolean. mostCommon(items) returns the element that appears most often (the one that appears first in the list when counts tie), or null for an empty list.

The supplied code reads a line of words and a line of numbers, and prints the number of words longer than 3 letters, the most common word, the number of even numbers, the most common number, and nothing: null for an empty list.

Your code goes in ListTools.kt. Main.kt holds the supplied input/output code and cannot be edited.

Try it yourself

fun main() {
    // Supplied input/output code: keep it as it is
    val input = generateSequence(::readLine).toList()
    val words = input[0].split(" ")
    val numbers = input[1].split(" ").map { it.toInt() }
    println("long words: ${countWhere(words) { it.length > 3 }}")
    println("most common word: ${mostCommon(words)}")
    println("even numbers: ${countWhere(numbers) { it % 2 == 0 }}")
    println("most common number: ${mostCommon(numbers)}")
    println("nothing: ${mostCommon(listOf<String>())}")
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming

Practice on your own: Kotlin playground