Higher-order functions

A higher-order function is a function whose argument or return value is a function. We have seen several higher-order functions before, such as:

// _Arrays.kt
public inline fun <T> Array<out T>.forEach(action: (T) - >Unit): Unit {
    for (element in this) action(element)
}
public inline fun <T> Array<out T>.filter(predicate: (T) - >Boolean): List<T> {
    return filterTo(ArrayList<T>(), predicate)
}
Copy the code

Where forEach requires that a function of type (T) -> Unit be passed as an argument (that is, one argument with no return value), Filter, on the other hand, requires a function of type (T) -> Boolean as a parameter (that is, it takes one argument and returns a Boolean value).

In Kotlin, the function is the first citizen.

Function types

What is a function type? We often use String, Int, etc. to describe the type of a variable, which can let us know exactly what the variable is, and function type is the same, it clearly describes the type of a variable to receive the function, function type format :(parameter 1 type, parameter 2 type,… , parameter n type)-> Return value type.

Note: When the return type is Unit, Unit cannot be omitted, as in: (String, String)->Unit.

Here’s an example:

val myFun1: (String) -> Unit = fun(value: String) { println(value) }
val myFun2: (String) -> Unit = { value -> println(value) }
Copy the code

In the above code, myFun1, a constant of type (String) -> Unit, receives an anonymous function of the same type, while myFun2 receives a lambda expression of the same type.

Lambda expressions are also anonymous functions.

Function reference

The concept of a reference object reference can be simply understood as a variable that points to a function, such as:

val myFun1: (String) -> Unit = fun(value: String) { println(value) }
val myFun2: (String) -> Unit = { value -> println(value) }
Copy the code

Here both myFun1 and myFun2 are function references. ForEach (T -> Unit); forEach (T -> Unit); forEach (T -> Unit);

fun main(args: Array<String>) {
    val myFun: (String) -> Unit = { value -> println(value) }
    args.forEach(myFun)
}
Copy the code

(T) -> Unit (T) -> Unit (T) -> Unit (T) -> Unit (T) -> Unit (T) -> Unit The function type should be (Int) -> Unit.

Here’s the question: If it’s not an anonymous function, but a named function, can you pass in the function name as an argument?

The function name is not a function reference, the function name is not a function reference, and the function name is not a function reference, but Kotlin provides the double colon :: qualifier to convert the function name to a function reference, so the code can be written like this:

fun myFun(value:String){
    println(value)
}
args.forEach(::myFun)
Copy the code

If you look carefully, you can see that println itself satisfies (T) -> Unit:

/** Prints the given [message] and the line separator to the standard output stream. */
@kotlin.internal.InlineOnly
public actual inline fun println(message: Any?). {
    System.out.println(message)
}
Copy the code

So, if we were just printing strings, we could have discarded the myFun layer and simply referred to prinln:

args.forEach(::println)
Copy the code

Reference member methods

So far, the function references we’ve used have been top-level, local, extension functions, not class member methods. If you refer to a class member method directly, you’ll get an error:

KFunction2

We know that top-level, local, and extension calls do not depend on the object instance, whereas the class member principle does. If a member method is referred to directly by the class name :: method name, (MyPrinter, String) -> Unit, (MyPrinter, String) -> Unit, Object instance :: method name is (String) -> Unit. Therefore, object instance reference member method can solve this problem:
,>

class MyPrinter{
    fun myFun(value: String) {
        println(value)
    }
}
fun main(args: Array<String>) {
    val myPrinter = MyPrinter()
    args.forEach(myPrinter::myFun)
}
Copy the code

Of course, this is not to say that methods in a class are always referred to in this way, but to consider whether method calls need to depend on an object instance, such as the Object or Companion Object methods, which can be called directly like functions because they do not depend on the object instance. So function types don’t have an extra Receiver argument, so they can be referenced directly by the class name :: method name:

object MyPrinter {
    fun myFun(value: String) {
        println(value)
    }
}

class MyPrinter {
    companion object {
        fun myFun(value: String) {
            println(value)
        }
    }
}

fun main(args: Array<String>) {
    args.forEach(MyPrinter::myFun)
}
Copy the code

Addendum: Methods can be thought of as a special type of function; Formally, functions with receivers are methods.

Function type instance call

A function reference can be made via its invoke(…) The operator calls f.invoke(x) or f(x) :

fun main(args: Array<String>) {
    val myFun1: (String) -> Unit = fun(value: String) { println(value) }
    myFun1.invoke("lqr") // lqr
    myFun1("lqr") // lqr
}
Copy the code

If you refer to a member method directly by the class name :: method, the type of the function referenced will add a Receiver at the first bit of the argument list, as in the case of the plus() method in String. Its Receiver is an instance of a String object:

val opear: (String, String) -> String = String::plus
val result = opear("hello"." world")// Equivalent to "hello". Plus (" world")
println(result) // hello world
Copy the code

In addition to being the first argument of a function type, Receiver can also be extracted via. With the original function type, the calling method will also change, similar to the extension function:

val opear: String.(String) -> String = String::plus
val result = "hello".opear(" haha")// Equivalent to "hello". Plus (" world")
println(result)
Copy the code