Lambda expressions and higher-order functions

In Kotlin, functions are the first citizens. Unlike In Java, functions must be written in a class. Functions in Kotlin can also be written in a file.

Lambda expressions are not new; Java 8 has them. It exists primarily to make code simpler.

Higher-order functions, the basic concept is very simple, is a function, also like other types of objects, as a function argument and return value.

This article is available at github.com/mengdd/Kotl…

Lambda expressions

Lambda expressions and anonymous functions are function literals, which are not declared in advance and are passed in as expressions.

Syntax for Lambda expressions

Composition of lambda expressions:

{parameter list -> function body}Copy the code

The parameter type can be omitted. If the function body returns a value other than Unit, the last expression is treated as the return value.

Example: setOnClickListener

Lambda expressions are used thanks in large part to modern ides, such as a simple code to set a listener to a Button,

Initially, as someone who uses Kotlin for Java 7, it might look something like this:

button.setOnClickListener(object : View.OnClickListener{
    override fun onClick(v: View?).{}})Copy the code

The object keyword is used here to declare an object of anonymous class.

But the IDE is not so happy to see this code, there is a yellow underline, Alt + Enter, “Convert to lambda”, Enter confirm, it looks like this:

button.setOnClickListener { }
Copy the code

The reason for this is that the View’s OnClickListener interface is a special type of interface: there is only one method.

This interface with only one method is called a functional interface, also known as a single abstract method type, or SAM.

PS: A bad example: If you have the misfortune to start your code like this:

button.setOnClickListener{object : View.OnClickListener {
    override fun onClick(v: View?).{}}}Copy the code

Note that the only difference from the above example is that the parenthesis () is changed to the curly brace {}. The program does not report an error, but when the button is clicked it should not execute the code you want.

The IDE will still prompt you to simplify, which will look like this:

button.setOnClickListener{ View.OnClickListener { } }
Copy the code

So every time a button is clicked, all you’re doing is creating an anonymous object.

The reason for this error, which the IDE does not, is that the lambda convention allows it to be written logically but syntactically correct. So let’s see what the convention is.

Lambda practice

  • If the function’s last argument receives the function, the passed lambda expression can be written outside the parentheses (this syntax is called trailing lambda). In this case, if lambda is the only argument, the parentheses can be omitted (the error example above is syntactically correct because this convention is followed).
  • If lambda has only one argument, you can use an implicit name without declaring itit.
  • Return value of a lambda expression: can be explicitreturnIf not explicitly returned, the value of the last expression is returned by default.
  • Parameters that are not used can be underlined_Said.

Anonymous functions

Lambda expressions do not explicitly specify return types, which may not be necessary in most cases because return types can be inferred automatically.

But if you really need to specify it explicitly, you can use anonymous functions.

fun(x: Int, y: Int): Int = x + y
Copy the code

An anonymous function looks like a normal function declaration, except it doesn’t have a name.

Unlike normal functions, parameter types can be omitted if they can be inferred from the context:

ints.filter(fun(item) = item > 0)
Copy the code

The return type inference for anonymous functions is the same as for normal functions.

Anonymous functions differ from Lambda:

  • Arguments to anonymous functions are always passed inside parentheses, and the simplified syntax of putting function arguments outside parentheses applies only to lambda expressions.
  • Unlabeled return statements: In lambda, return the outer function; in anonymous functions, return the function.

Higher-order functions

Higher-order Functions: Functions that take Functions as arguments or return values.

Function types

Function types: Functions can be classified into a Function type based on arguments and return values.

The writing of function types

The basic writing of a function type is a list of arguments in parentheses and a return value. For example, (A, B) -> C is A function type that takes arguments of both types A and B and returns A return value of type C.

  • The argument list can be empty, for example() -> A.
  • The return value isUnitCan not be omitted.
  • Function types can also write receiver types:A.(B) -> C. Indicates that the function is called on type A.
  • suspendThe keyword represents a special type of function, so if there is,suspendQualifiers also appear. Such assuspend () -> Unit.
  • You can also add parameter names to express the meaning of parameters. Such as(x: Int, y: Int) -> Point.

A few special points:

  • Function types also have nullable types, parentheses and?. Such as((Int, Int) -> Int)?.
  • Function types can be high-order, combined with parentheses, as in:(Int) -> ((Int) -> Unit).
  • The arrows follow the right associative principle, which means the operations go from right to left, so(Int) -> (Int) -> Unitand(Int) -> ((Int) -> Unit)It means the same thing.

A function type can be given a type alias, such as:

typealias ClickHandler = (Button, ClickEvent) -> Unit
Copy the code

Instantiate the function type

There are several ways to instantiate a function type:

  • Blocks of code with function literals: lambda expressions, anonymous functions.
  • Use existing declaration references: functions, attributes, constructors.: :See Synonyms atCallable Reference.
  • A function type can also be implemented as an interface by a class, so creating an instance of that class creates an instance of the function type.

Invokes an instance of a function type

Function type declared, instantiated, how to call? You can use the invoke operator, or you can call it directly by name.

If there is a receiver type, then the receiver object needs to be the first argument. Alternatively, you can place the receiver object at a point (.) Previously, it is called like an extension function.

val stringPlus: (String, String) -> String = String::plus
val intPlus: Int. (Int) - >Int = Int::plus

println(stringPlus.invoke("< -"."- >"))
println(stringPlus("Hello, "."world!")) 

println(intPlus.invoke(1.1))
println(intPlus(1.2))
println(2.intPlus(3)) // extension-like call
Copy the code

reference

  • Higher-Order Functions and Lambdas
  • Functions
  • Returns and Jumps
  • Callable Reference