Kotlin detailed article notes collation update progress: Kotlin Series – High order functions and Common functions in standard Library (3) Kotlin Series – Advanced Deep Generic Covariant Inversion from Java to Kotlin(4) Koltin series – Coroutines from Recognition to Use in Android (5)

1. Higher-order functions

Basic concepts: functions that pass in or return functions

Function reference: The name of the referenced function is preceded by ::

  • There are several types:
  • Class member method references:Class name :: Member method name
  • Extension function references:Class name :: Extension function name
  • Instance function references:Instance name :: Member method name
  • Package-level function references:: : the function name

First example:

Prints the elements in the array (passing in package-level functions)

Fun main(args:Array<String>) {args. ForEach (::println)} public actual inline fun println(message: Any?) { System.out.println(message) } public inline fun <T> Array<out T>.forEach(action: (T) -> Unit): Unit {for (element in this) action(element)
}
Copy the code

ForEach (action: (T) -> Unit) : a function with (action: (T) -> Unit), type T, and return Unit println(message: Any?). ForEach (::println) we call args. ForEach (::println) to pass println to forEach

Second example:

Filter an empty string in an array (passing in a class member function)

fun main(args:Array<String>) {
    args.filter(String::isNotEmpty)
}

public inline fun <T> Array<out T>.filter(predicate: (T) -> Boolean): List<T> {
    return filterTo(ArrayList<T>(), predicate)
}

public inline fun CharSequence.isNotEmpty(): Boolean = length > 0

Copy the code

One thing to note here: filter requires the type of the function to be (predicate: (T) -> Boolean), but we pass String::isNotEmpty with no arguments!! Public inline fun charsequent. isNotEmpty(): Boolean = length > 0 Public inline fun charsequent. isNotEmpty(): Boolean = length > 0

Answer: Because the class name :: member method name takes one parameter by default, the function type is that of the class name. For example, String::isNotEmpty equals isNotEmpty(String)

Third example:

Print the elements in the array (pass in the instance function)

fun main(args:Array<String>) {
    val t = Test()
    args.forEach(t::testName)
}
class Test{
    fun testName(name:String){
        println(name)
    }
}
public inline fun <T> Array<out T>.forEach(action: (T) -> Unit): Unit {
    for (element in this) action(element)
}
Copy the code

T ::testName is passed. The instance name :: member method name does not have an extra parameter by default. If you use Test::testName, an error message will be displayed, which verifies that the class name :: member method name takes one parameter by default.

To summarize, a function reference is a function passed as a parameter variable to a specific method, or it can be assigned to a variable. Note that if it is a class member function, extension function reference (class name: function name), the default argument is the class itself

2. The closure

  • The environment in which the function runs
  • Holds the running state of the function
  • Functions/classes can be defined inside functions
fun add(x: Int): (Int) -> Int {
    return fun(y: Int): Int {
        return x + y
    }
}
fun main() {
    var add2 = add(2)
    println(add2(10))
}
Copy the code

The function definition method can pass in the function, can also return the function, the function scope contains the function’s subfunctions and subclasses. Format: fun Method name (parameter: function type) Function type {} Function type Basic writing: () -> Unit (multiple parameters) -> return type

3. Function composition

  • The f(g(x)) function passes in the function
Val multiplyBy2 = {I: Int -> I * 2main() {
    println(multiplyBy2(add5(9)))
}
-----打印出来的Log
28
Copy the code

So that’s the basic demonstration, function passing in function. Let’s extend the function:

Val multiplyBy2 = {I: Int -> I + 5} val multiplyBy2 = {I: Int -> I * 2} val sum = {q: Int, w: Infix fun <P1, P2, R> Function1<P1, P2>.function: Function1<P2, R>): Function1<P1, R> {
    return fun(p1: P1): R {
        returnFunction.invoke (this.invoke(p1))}} infix fun < p1,P2,R> Function1<P2,R>.function:Function1<P1,P2>):Function1<P1,R>{
    return fun (p1:P1):R{
        returnThis. Invoke (function.invoke(p1))}} 3: fun < p1, P2, P3, R> Function2<P2, P3, R>.toallsum (function: Function1<P1, P2>,
    function1: Function1<P2, P3>
): Function2<P1, P2, R> {
    return fun(p1: P1, p2: P2): R {
        return this.invoke(function.invoke(p1), function1.invoke(p2))
    }
}

fun main() {add5AndMulti2 = add5 andThen multiplyBy2 Val add5ComposeMulti2 = add5 compose multiplyBy2  val sum = sum.toAllSum(add5, MultiplyBy2) println(add5AndMulti2(10)) println(add5ComposeMulti2(10)) println(sum(10,10))} ----- Printout Log 30 25 35Copy the code

The above is actually the extension function, and then pass in the function and return function, only one argument using the infix keyword.

Key points 1, 2, and 3 all extend function types. Key points 1 and 2 extend function types by passing in one function parameter, and key point 3 extension function by passing in two function parameters

For example: keypoint 1: Function extends Function andThen, passes Function type Function1 , returns Function type Function1 R Second return:function.invoke(this.invoke(p1)), call this.invoke(p1) and pass the value returned here to function.invoke(). The function before andThen is called, and the function after andThen is called. You can customize various extension functions according to these methods ,> ,> ,p2>

Run, let, with, apply, also

  • Start withrunFor example,
Public inline fun <R> run(block: () -> R): R {contract {callsInPlace(block, invocationKind.exactly_once)}returnPublic inline fun <T, R> t. run(block: T.() -> R): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }return block()
}
Copy the code

T.run(block: () -> R): R; T: T () -> R): R; T: T () -> R): R T.() -> R, which is a reference to the caller itself, can be used directly in a block to use member variables and functions in T

Var sum = run {5+3} println(sum) var list = arrayListOf("Xiao Ming"."Little red"."Black") var aListSize = aList. Run {size} println (aListSize) -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - 8 3 printCopy the code

This contract {… } don’t understand can temporarily don’t tube it, a written contract in kotlin, details can see kotlinlang.org/docs/refere…

  • with,apply,also,let
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}
public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}
public inline fun <T> T.also(block: (T) -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block(this)
    return this
}
public inline fun <T, R> T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}
Copy the code
  • with: Accepts two parameters, one is itself, one isblock: T.() -> RTo return toreturn receiver.block();

    withUsage:
var aList = arrayListOf("Xiao Ming"."Little red"."Black")
var l = with(aList){
  add("Yellow")
  removeAt(0)
  forEach {
    print("$it, ""Size)}} println (l) -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - print small red, black, yellow, and 3Copy the code
  • apply: Extension function of the method, passed inblock: T.() -> Unit, returns the caller itself, used withrunConsistent, but ultimately returns the caller itself.
  • also: Extension function of the method, passed inblock: (T) -> UnitThe first few methods are a little bit different here,blockIntroduced to theTThe caller itself, and the function returns the caller itself.alsoUsage:
var aList = arrayListOf("Xiao Ming"."Little red"."Black")
val  sizeFinally = aList.also {
  println(it.size)
  it.add("Yellow")
  it.add("Little green")}.size println(sizeFinally) --------- Print 3 5Copy the code
  • let: extension function of the method, passed inblock: (T) -> R.letMethod returnsR.letUsage:
val  sizeFinally = aList.let {
        println(it.size)
        it.add("Yellow")
        it.add("Little green") it. Size} println (sizeFinally) -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- print 3 5Copy the code

Supplement: Tailrecursively optimized tailrec

  • willtailrecKeyword added tofunPreprompt compiler tail-recursive optimization.Tail recursion: A form of recursion in which there is no other operation after calling itself.
  • The relation between tail recursion and iteration: Tail recursion can be directly converted to iteration.
Tailrec funFindListNode (head: ListNode? , value: Int): ListNode? { head ? :return null
    if (head.value == value) return head
    returnFindListNode (head.next, value)} // Factorial (n:Long):Long{return n * factorial(n-1)
}
Copy the code

The first of the above methods conforms to the form of tail recursion, which we can add the keyword tailrec, what is the advantage?

fun main() {
    var listNode = ListNode(0)
    var p =listNode
    for (i in1.. 100000) { p.next = ListNode(i) p = p.next!! } println(findListNode(listNode,99998)? .value)} ----------- add the keyword tailrec - print Log 99998 ---------- do not add the keyword print Log Exceptionin thread "main" java.lang.StackOverflowError
Copy the code

Because of the tailrec keyword, the optimization is actually iterative and reduces the memory space overhead compared to recursion.