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
NONEKotlin 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 IntChallenge
EasyWrite 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>())}")
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Classes and Objects
Working With FilesDefining ClassesPrimary Constructorsinit Blocks and ValidationSecondary ConstructorsRecap - Library Card4Inheritance
Open Classes and SubclassesOverriding Memberssuper and ConstructorsAbstract ClassesRecap - Staff Directory2Properties and Encapsulation
Custom AccessorsVisibility ModifiersPrivate Setterslazy and lateinitRecap - Bank Account5Interfaces
Declaring InterfacesDefault MethodsMultiple InterfacesInterfaces as TypesRecap - Payment Methods8Generics
Generic FunctionsGeneric ClassesType ConstraintsVariance With in and outRecap - Priority Queue11Project: Library Management
Books and MembersBorrowing BooksPractice on your own: Kotlin playground