layout: post
title: "kotlin-高阶函数"
subtitle: "这个世界属于有天赋的人 也属于认真的人 更属于那些在有天赋的领域认真钻研的人"
高阶函数
高阶函数:传入或者返回函数的函数
函数引用的三种方式
- 包级函数作为高阶函数的参数的时候,直接按照参数返回值来判断是否合适.
- 类的成员函数(带有Receiver的函数)作为高阶函数的参数的时候,需要使用实例来进行引用.
- 扩展方法作为高阶函数的参数的时候,需要注意的是扩展方法有一个默认的参数就是实例本身
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
- map:集合的映射
- map:集合的转换
val arrayInt1 = arrayInt.map { it * 4 }
val arrayDouble = arrayInt.map(Int::toDouble)
println(arrayInt1)
println(arrayDouble)
flatMap
- flatMap:扁平化集合
- ... RangeTo
- 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
- 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
- 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
- filter用于过滤,传入表达式的值为true时保留:
val pm = MyPrintln()
arrayStr.filter { it.contains("子") }.forEach(pm::println)
arrayStr.filter { it.length > 4 }.forEach(pm::println)
takeWhile
- 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
}
}
}
}
闭包
- 函数运行的环境
- 持有函数运行状态
- 函数内部可以定义函数
- 函数内部也可以定义类
复合函数
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)
偏函数
传入部分参数得到新函数