
This is a chapter from the book Functional Kotlin. You can find it on LeanPub or Amazon. It is also available as a course.
invoke method. However, functions can have different parameters and result types, so there are many possible function types.->) and the result type. Since all functions in Kotlin need to have a result type, a function that does not return anything significant should declare Unit[^02_1] as its result type.
() -> Unit- the simplest function type, representing a function that expects no arguments and returns nothing significant[^02_5].(Int) -> Unit- a function type representing a function that expects a single argument of typeIntand returns nothing significant.(String, String) -> Unit- a function type representing a function that expects two arguments of typeStringand returns nothing significant.() -> User- a function type representing a function that expects no arguments and returns an object of typeUser.(String, String) -> String- a function type representing a function that expects two arguments of typeStringand returns an object of typeString.(String) -> Name- a function type representing a function that expects a single argument of typeStringand returns an object of typeName.
Boolean, like (T) -> Boolean, are often named predicate. Functions that transform one value to another, like (T) -> R, are often called transformation. Functions that return Unit, like (T) -> Unit, are often called operation.invoke. Its parameters and result type are the same as defined by the function type.fun fetchText( onSuccess: (String) -> Unit, onFailure: (Throwable) -> Boolean ) { // ... onSuccess.invoke("Some text") // returns Unit // or val handled: Boolean = onFailure.invoke(Error("Some error")) }
invoke is an operator[^02_4], we can "call an object" that has this method. This is an implicit invoke call.fun fetchText( onSuccess: (String) -> Unit, onFailure: (Throwable) -> Boolean ) { // ... onSuccess("Some text") // returns Unit // or val handled: Boolean = onFailure(Error("Some error")) }
invoke calls are more readable for less experienced developers. An implicit call is shorter and, from a conceptual perspective, it better represents calling an object.invoke.fun someOperations( onStart: (() -> Unit)? = null, onCompletion: (() -> Unit)? = null, ) { onStart?.invoke() // ... onCompletion?.invoke() }
class Button(val text: String, val onClick: () -> Unit) var listeners: List<(Action) -> Unit> = emptyList() fun setListener(listener: (Action) -> Unit) { listeners = listeners + listener }
(() -> Unit) -> Unit- a function type representing a function that expects a function type() -> Unitas an argument and returns nothing significant.() -> () -> Unit- a function type representing a function that expects no arguments and returns a function type() -> Unit.
fun setListItemListener( listener: (Int, Int, View, View) -> Unit ) { listeners = listeners + listener }


fun setListItemListener( listener: ( position: Int, id: Int, child: View, parent: View ) -> Unit ) { listeners = listeners + listener }


setListItemListener example, where the same function type is repeated in the listener property and the removeListItemListener function.private var listeners = emptyList<(Int, Int, View, View) -> Unit>() fun setListItemListener( listener: ( position: Int, id: Int, View, parent: View ) -> Unit ) { listeners = listeners + listener } fun removeListItemListener( listener: (Int, Int, View, View) -> Unit ) { listeners = listeners - listener }
typealias keyword. We then specify a name, followed by the equals sign (=), and we then specify which type should stand behind this name. Defining a type alias is like giving someone a nickname. It is not really a new type: it’s just a new way to reference the same type. Both types can be used interchangeably because types generated with type aliases are replaced with their definitions during compilation.typealias Users = List<User> fun updateUsers(users: Users) {} // during compilation becomes // fun updateUsers(users: List<User>) {} fun main() { val users: Users = emptyList() // during compilation becomes // val users: List<User> = emptyList() val newUsers: List<User> = emptyList() updateUsers(newUsers) // acceptable }
import thirdparty.Name class Foo { val name1: Name val name2: my.Name }
import my.Name typealias ThirdPartyName = thirdparty.Name class Foo { val name1: ThirdPartyName val name2: Name }
// DON'T DO THAT! Misleading and false type safety typealias Minutes = Int typealias Seconds = Int fun decideAboutTime(): Minutes = 10 fun setupTimer(time: Seconds) { /*...*/ } fun main() { val time = decideAboutTime() setupTimer(time) }
class OnClick : (Int) -> Unit { override fun invoke(viewId: Int) { // ... } } fun setListener(l: (Int) -> Unit) { /*...*/ } fun main() { val onClick = OnClick() setListener(onClick) }
Unit is an object with a single value that can be used within generic types. A function with the return type Unit is equivalent to a Java method that declares void.[^02_2]: A method is a function associated with a class; it is called on an object, so both member and extension functions are methods.
[^02_3]: More about types in Kotlin Essentials, Typing system chapter.
[^02_4]: More about operators in Kotlin Essentials, Operators chapter.
[^02_5]: Those who have read the Typing system chapter from Kotlin Essentials might have guessed why I describe
Unit as "nothing significant" instead of "nothing". Functions in Kotlin can indeed return nothing; in such cases, they declare Nothing as a result type, but this has a very different meaning than Unit.[^02_6]: Protecting ourselves from type misuse is better described in Effective Kotlin, Item 52: Consider using inline value classes.
[^02_7]: The example was proposed by Endre Deak.
