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 ,p2>, 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 ,>
Run, let, with, apply, also
- Start with
run
For 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.() -> R
To return toreturn receiver.block()
;
with
Usage:
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 withrun
Consistent, but ultimately returns the caller itself.also
: Extension function of the method, passed inblock: (T) -> Unit
The first few methods are a little bit different here,block
Introduced to theT
The caller itself, and the function returns the caller itself.also
Usage:
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
.let
Method returnsR
.let
Usage:
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
- will
tailrec
Keyword added tofun
Preprompt 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.