kotlin从入门到看开 ₆


layout: post
title: "kotlin-高阶函数"
subtitle: "这个世界属于有天赋的人 也属于认真的人 更属于那些在有天赋的领域认真钻研的人"


高阶函数

高阶函数:传入或者返回函数的函数

函数引用的三种方式

  1. 包级函数作为高阶函数的参数的时候,直接按照参数返回值来判断是否合适.
  2. 类的成员函数(带有Receiver的函数)作为高阶函数的参数的时候,需要使用实例来进行引用.
  3. 扩展方法作为高阶函数的参数的时候,需要注意的是扩展方法有一个默认的参数就是实例本身
fun test() {
        val line = "-------"
        val arrayInt = listOf(11, 22, 33, 44, 55)
        var arrayStr = listOf("杏本诗歌", "守凪了子", "西宫结弦", "花畑佳子", "冈部伦太郎", "")
        /**
         * 包级函数
         */
        arrayInt.forEach(::println)
        println(line)
        /**
         * 类的成员函数
         */
        val pm = MyPrintln()
        arrayStr.filter { it.contains("子") }.forEach(pm::println)
        arrayStr.filter { it.length > 4 }.forEach(pm::println)
        println(line)
         /**
         * 扩展方法
         */
        arrayStr.filter(String::isNotBlank).forEach(::println)
        println(line)
    }

包级函数

map

  1. map:集合的映射
  2. map:集合的转换
        val arrayInt1 = arrayInt.map { it * 4 }
        val arrayDouble = arrayInt.map(Int::toDouble)
        println(arrayInt1)
        println(arrayDouble)

flatMap

  1. flatMap:扁平化集合
  2. ... RangeTo
  3. 1..5:返回一个intRange(1,2,3,4,5)
        val list = arrayListOf(1..9, 233..255, 79..88)
        val flatMap = list.flatMap { it }
        flatMap.forEach(::println)

reduce

  1. reduce通常用于求和
        println(flatMap.reduce(Int::plus))
        println(flatMap.reduce { acc, i -> acc + i })
        //阶乘示例
        println(factorial(8))

阶乘

    /**
     * 阶乘
     */
    private fun factorial(n: Int): Int {
        if (n == 0) return 1
        return (1..n).reduce { acc, i -> acc * i }
    }

flod

  1. fold:对集合进行自定义计算
        println((0..1).fold(8) { acc, i -> acc + i })

        /**
         * joinToString:对集合进行转化和拼接
         */
        println((0..10).joinToString(","))
        val fold = (0..5).fold(StringBuilder()) { acc, i -> acc.append(i).append(",") }
        println(fold)

字符串拼接

        println((0..10).joinToString(","))

filter

  1. filter用于过滤,传入表达式的值为true时保留:
        val pm = MyPrintln()
        arrayStr.filter { it.contains("子") }.forEach(pm::println)
        arrayStr.filter { it.length > 4 }.forEach(pm::println)

takeWhile

  1. takeWhile通常用于带有条件的循环遍历,直到第一个不满足条件元素出现循环结束.
         //直到第一个不小于44的元素出现,循环结束.
         //最后输出元素应为(11,22,33)
        val arrayInt = listOf(11, 22, 33, 44, 55)
        arrayInt.takeWhile { it<44 }.forEach(::println)

补充说明

  • 包级函数:在包内直接声明函数的方式叫作包级函数
package com.litton.ishir

inline fun print(message: Int) {
    System.out.println(message)
}

data class

    data class singer(var name: String, var song: String) {
        fun sing() {
            println("$name,演唱新单曲$song")
        }
    }

let

  • let: it表示引用对象,可调用其成员(属性以及方法),it不可省略.
  • 返回值: 返回值为函数块的最后一行,为空就返回一个Unit类型的默认值.

使用:

    
KaryNg.let {}   //在函数体内使用it替代引用对象去访问其公有的属性和方法
KaryNg?.let {}  //另一种用途:?判断对象是否为空,不为空才会执行let函数体

例子:

    fun main(args: Array) {
        val KaryNg = singer("吴雨霏", "我本人")
        val kary: Any = KaryNg?.let {
            it.song
            it.name.length
        }
        println(kary)
    }

输出:

singer(name=吴雨霏, song= <<我本人>>)

3

适用场景:

使用let函数处理需要针对一个可null的对象统一做判空处理.

官方源码:

/**
 * Calls the specified function [block] with `this` value as its argument and returns its result.
 */
@kotlin.internal.InlineOnly
public inline fun  T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}

also

  • also: it表示引用对象,可调用其成员(属性以及方法),it不可省略.
  • 返回值: 返回值为函数块的最后一行,为空就返回一个Unit类型的默认值.

使用:

also函数结构实际上和let很像唯一的区别就是返回值不一样,let以闭包的形式返回,返回函数体内最后一行的值,如果最后一行为空就返回一个Unit类型的默认值.而also函数则返回传入对象本身.

        KaryNg.run {  }

例子:

    fun main(args: Array) {
        val KaryNg = singer("吴雨霏", "我本人")
        val kary: Any = KaryNg?. also {
            it.song
            it.name.length
        }
        println(kary)
    }

输出:

吴雨霏,演唱新单曲 <<我本人>>

singer(name=吴雨霏, song= <<我本人>>)

适用场景:

一般可用于多个扩展函数链式调用

官方源码:

/**
 * Calls the specified function [block] with `this` value as its argument and returns `this` value.
 */
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun  T.also(block: (T) -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block(this)
    return this
}

with

  • with: 在函数块内通过 this 指代对象,调用成员(属性以及方法),this可省略.
  • 返回值: 返回值为函数块的最后一行,为空就返回一个Unit类型的默认值.

使用:

with函数和前面的函数使用方式略有不同,它不是以扩展函数.它将对象作为函数的参数

        with(KaryNg){ sing() }

例子:

    fun main(args: Array) {
        val KaryNg = singer("吴雨霏", "我本人")
        val sing=with(KaryNg){
            println(this)
            sing()
            name
        }
            println(sing)
    }

输出:

singer(name=吴雨霏, song= <<我本人>>)

吴雨霏,演唱新单曲 <<我本人>>

吴雨霏

适用场景:

适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类方法即可,经常用于Android中RecyclerView中onBinderViewHolder中,数据model的属性映射到UI上

官方源码:

/**
 * Calls the specified function [block] with the given [receiver] as its receiver and returns its result.
 */
@kotlin.internal.InlineOnly
public inline fun  with(receiver: T, block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}

run

  • run: 在函数块内通过 this 指代对象,调用成员(属性以及方法),this可省略.
  • 返回值: 返回值为函数块的最后一行,为空就返回一个Unit类型的默认值.

使用:

        KaryNg.run {  }

例子:

    fun main(args: Array) {
        val KaryNg = singer("吴雨霏", "我本人")
        val kary: Any = KaryNg.run {
                println(this)
                sing()
                name.length
            }
            println(kary) 
     }

输出:

singer(name=吴雨霏, song= <<我本人>>)

吴雨霏,演唱新单曲 <<我本人>>

3

适用场景:

适用于let,with函数任何场景.run函数是let,with两个函数结合体,准确来说它弥补了let函数在函数体内必须使用it参数替代对象,在run函数中可以像with函数一样可以省略,直接访问实例的公有属性和方法,另一方面它弥补了with函数传入对象判空问题,在run函数中可以像let函数一样做判空处理

官方源码:

/**
 * Calls the specified function [block] with `this` value as its receiver and returns its result.
 */
@kotlin.internal.InlineOnly
public inline fun  T.run(block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

apply

  • with: 在函数块内通过 this 指代对象,调用成员(属性以及方法),this可省略.
  • 返回值: 返回值为函数块的最后一行,为空就返回一个Unit类型的默认值.

使用:

从结构上来看apply函数和run函数很像,唯一不同就是它们返回值不一样,run函数以闭包形式返回最后一行代码的值,而apply函数的返回传入对象本身

        KaryNg.apply {  }

例子

    fun main(args: Array) {
        val KaryNg = singer("吴雨霏", "我本人")
        val kary: Any = KaryNg.apply {
            song
            sing()
        }
        println(kary)
    }

输出

吴雨霏,演唱新单曲我本人

singer(name=吴雨霏, song=我本人)

适用场景:

apply一般用于一个对象实例初始化的时候,需要对对象中的属性进行赋值.或者动态inflate出一个XML的View的时候需要给View绑定数据也会用到,这种情景非常常见.特别是在我们开发中会有一些数据model向View model转化实例化的过程中需要用到.

官方源码

/**
 * Calls the specified function [block] with `this` value as its receiver and returns `this` value.
 */
@kotlin.internal.InlineOnly
public inline fun  T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}

use

未完结

  • use: 在函数块内通过 it 指代对象,调用成员(属性以及方法),it不可省略.
  • 返回值: 返回值为函数块的最后一行,为空就返回一个Unit类型的默认值.

例子

 val read = BufferedReader(FileReader("build.gradle")).use {
            val readText = it.readLine()
            readText
        }
        println(read)

输出

apply plugin: 'com.android.application'

适用场景:

use自己关流.

官方源码

/**
 * Executes the given [block] function on this resource and then closes it down correctly whether an exception
 * is thrown or not.
 *
 * @param block a function to process this [Closeable] resource.
 * @return the result of [block] function invoked on this resource.
 */
@InlineOnly
@RequireKotlin("1.2", versionKind = RequireKotlinVersionKind.COMPILER_VERSION, message = "Requires newer compiler version to be inlined correctly.")
public inline fun  T.use(block: (T) -> R): R {
    var exception: Throwable? = null
    try {
        return block(this)
    } catch (e: Throwable) {
        exception = e
        throw e
    } finally {
        when {
            apiVersionIsAtLeast(1, 1, 0) -> this.closeFinally(exception)
            this == null -> {}
            exception == null -> close()
            else ->
                try {
                    close()
                } catch (closeException: Throwable) {
                    // cause.addSuppressed(closeException) // ignored here
                }
        }
    }
}

闭包

  1. 函数运行的环境
  2. 持有函数运行状态
  3. 函数内部可以定义函数
  4. 函数内部也可以定义类

复合函数

m(x) = f(g(x))

  • g = g(x) P1:即g函数的参数{x} P2:即g函数的返回值{g}== P2
  • f = f(g) P2:即f函数的参数{g}== P2 R:即f函数的返回值{f}== R
  • m = m(x) P1:即g函数的参数{x}
    fun onTest() {
        //m(x) = f(g(x))
        val g = { i: Int -> i + 7 } //g(x) = x+7
        val f = { i: Int -> i * 2 } //f(g) = g*2
        val m = g composite f //m(x) = (x+7)*2
        val m1 = f composite g //m(x) = (x*2)+7
        val value = m(2)
        val value1 = m1(2)
        println(value)
        println(value1)
    }

/**
 * @param P1
 * @param P2
 * @param R
 */
infix fun  Function1.composite(function: Function1): Function1 {
    return fun(p1: P1): R {
        return function.invoke(this.invoke(p1))
    }
}

柯里化

多元函数变换成一元函数调用链

fun log(tag: String) = fun(target: OutputStream) = fun(message: Any) = target.write("$tag-$message\n".toByteArray())
 
fun log1(tag:String,target:OutputStream,message: Any){
    target.write("$tag-$message \n".toByteArray())
}
 
fun main(args: Array) {
    log("日志")(System.out)("记录日志1")
    ::log1.curried()("日志")(System.out)("记录日志2")
 
}
 
fun  Function3.curried() = fun(p1: P1) = fun(p2: P2) = fun(p3: P3) = this(p1, p2, p3)

偏函数

传入部分参数得到新函数

你可能感兴趣的:(kotlin从入门到看开 ₆)