高阶函数

将函数用作参数或者返回值

image.png

函数类型

  • 通用 (A, B) -> C
  • 额外接收者 A.(B) -> C
  • (暂时不用看)挂起函数 suspend () -> Unit 或者 suspend A.(B) -> C
  1. fun <T, N> T.getNameIndex(block: (T) -> N): N {
  2. return block(this)
  3. }
  4. val str = "haha1"
  5. val index = str.getNameIndex {
  6. if (it == "haha") {
  7. 11
  8. } else {
  9. 22
  10. }
  11. }

闭包

闭包就是能够读取其他函数内部变量的函数。
可以理解为: 定义在一个函数内部的函数
本质上:闭包将函数内部和函数外部连接起来的桥梁

it 和 this

it: 单个参数的隐式名称
如果编译器可以识别出签名,那么就可以不用声明 而使用 it

ints.filter { it > 0 } // 这个字面值是“(it: Int) -> Boolean”类型的

this: 代表当前在那个对象里调用。最近一层的对象。

let、with、run、apply、also

内联函数

let :

1、最常用的场景就是 使用let函数处理一个可null的对象,统一做判空处理
2、明确一个变量的作用域范围

public inline fun <T, R> T.let(block: (T) -> R): R = block(this)
object.let{
   it.todo()//在函数体内使用it替代object对象去访问其公有的属性和方法
   ...
}

//另一种用途 判断object为null的操作
object?.let{//表示object不为null的条件下,才会去执行let函数体
   it.todo()
}

with

适用一个类的多个方法时,可以省去类名重复。

public inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
 with(object){
   //todo
 }

省去了 item 的一直写。

override fun onBindViewHolder(holder: ViewHolder, position: Int){
   val item = getItem(position)?: return

   with(item){

      holder.tvNewsTitle.text = StringUtils.trimToEmpty(titleEn)
       holder.tvNewsSummary.text = StringUtils.trimToEmpty(summary)
       holder.tvExtraInf.text = "难度:$gradeInfo | 单词数:$length | 读后感: $numReviews"
       ...   

   }

}

run

let 和 with 的结合题。内部不用在用 it 了。 run返回的是最后一行的值

public inline fun <T, R> T.run(block: T.() -> R): R = block()
object.run{
//todo
}

apply

apply 和run 的返回值不同。
run 返回的是最后一行的值
apply 返回的是传入对象本身

public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }

also

和 let 不同的是返回值
let : 返回最后一行
alse:返回传入对象本身

public inline fun T.also(block: (T) -> Unit): T { block(this); return this }
函数名 定义inline的结构 函数体内使用的对象 返回值 是否是扩展函数 适用的场景
let fun T.let(block: (T) -> R): R = block(this) it指代当前对象 闭包形式返回 适用于处理不为null的操作场景
with fun with(receiver: T, block: T.() -> R): R = receiver.block() this指代当前对象或者省略 闭包形式返回 适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可,经常用于Android中RecyclerView中onBinderViewHolder中,数据model的属性映射到UI上
run fun T.run(block: T.() -> R): R = block() this指代当前对象或者省略 闭包形式返回 适用于let,with函数任何场景。
apply fun T.apply(block: T.() -> Unit): T { block(); return this } this指代当前对象或者省略 返回this 1、适用于run函数的任何场景,一般用于初始化一个对象实例的时候,操作对象属性,并最终返回这个对象。
2、动态inflate出一个XML的View的时候需要给View绑定数据也会用到.
3、一般可用于多个扩展函数链式调用
4、数据model多层级包裹判空处理的问题
also fun T.also(block: (T) -> Unit): T { block(this); return this } it指代当前对象 返回this 适用于let函数的任何场景,一般可用于多个扩展函数链式调用

Null

?. 安全调用运算符

s?.toUpperCase()   
等价于 
if(s!=null){
    s.toUpperCase()
}else{
    null
}

?: Elvis运算符

当为null的时候,用来代替返回 null ,换成我们想要的返回值

 strs.forEach {
   val str = it?.apply {
       it+"heng"
       }?:"哈喽"
       println(str)
   }

as? 安全转换运算符

            val a = "hahhhh"
            val h = a as? String ?: return
            println(h)

!! 非空断言

如果百分比确定 建议就别用这个了

平台类型

对于 kotlin 和 java 的交互,通过注解可以进行转换,如果没有注解,那么kotlin中要像java中一样,进行手动判断。

image.png

数字转换

image.png

Any Unit Nothing

Any

kotlin中所有非null类型的父类 相当于java的Object。
但是 如果想要 wait notify 等方法 需要进行转换
Any 和 Any?

Unit

所有不显示声明返回类型的函数都会返回 Unit 类型。 只有一个值 Unit

Nothing

如果是异常 它其实不应该有类型返回,用 Unit 就不恰当了,所以用 Nothing

fun fail() {
    throw RuntimeException("Something went wrong")
}
//textView 就成了非空类型了
val data: String = intent.getStringExtra("key") ?: fail()
textView.text = data

委托

类委托

相当于代理类 很好理解 也很好用

属性代理

lazy

延时属性

val lazyValue: String by lazy {
    println("computed!")
    "Hello"
}

fun main() {
    println(lazyValue)
    println(lazyValue)
}

observable

可观察

import kotlin.properties.Delegates

class User {
    var name: String by Delegates.observable("<no name>") {
        prop, old, new ->
        println("$old -> $new")
    }
}

fun main() {
    val user = User()
    user.name = "first"
    user.name = "second"
}

map

把多个属性储存在一个映射(map)中,而不是每个存在单独的字段中

class User2(val map: Map<String, Any?>) {
    val name: String by map
    val age: Int     by map
}
val user = User(mapOf(
    "name" to "John Doe",
    "age"  to 25
))
println(user.name) // Prints "John Doe"
println(user.age)  // Prints 25

委托给另一个属性

这是很有用的,例如,当想要以一种向后兼容的方式重命名一个属性的时候:引入一个新的属性、 使用 @Deprecated 注解来注解旧的属性、并委托其实现。

class MyClass {
   var newName: Int = 0
   @Deprecated("Use 'newName' instead", ReplaceWith("newName"))
   var oldName: Int by this::newName
}

fun main() {
   val myClass = MyClass()
   // 通知:'oldName: Int' is deprecated.
   // Use 'newName' instead
   myClass.oldName = 42
   println(myClass.newName) // 42
}

泛型

约束

多重约束

class Test<T: B>{

}
class Test<T> where T: A, T: B{

}

型变

https://juejin.cn/post/6847902219140857870

image.png

协变

如果 List<String> List<Object> 的子类型。 那么就是型变。
java 中是不支持型变的。

在泛型类型 或者 泛型方法的 参数前面加 out 其实就是输出位置 方法返回的位置

//定义实体类关系
open class Flower
class WhiteFlower: Flower(){}
class ReaFlower: Flower(){}

//生产者
interface Product<out T> {
     fun produce(): T
}

class WhiteFlowerProduct<WhiteFlower> {
    //将泛型类型作为返回
    override fun produce(): WhiteFlower {
       return WhileFlower();
    }
}

//如下编译通过
val product: Product<Flower> = WhiteFLowerProduct()

逆变

in

协变 逆变 不变
Kotlin 实现方式: 只能作为消费者,只能读取不能写入 实现方式 只能添加,读取受限 实现方式:, 可读可写
Java 实现方式:<? extends T> 只能作为消费者,只能读取不能写入 实现方式<? super T> 只能添加,读取受限 实现方式:, 可读可写

参考:
[1] Kotlin系列之let、with、run、apply、also函数的使用