高阶函数
将函数用作参数或者返回值
函数类型
- 通用 (A, B) -> C
- 额外接收者 A.(B) -> C
- (暂时不用看)挂起函数 suspend () -> Unit 或者 suspend A.(B) -> C
fun <T, N> T.getNameIndex(block: (T) -> N): N {
return block(this)
}
val str = "haha1"
val index = str.getNameIndex {
if (it == "haha") {
11
} else {
22
}
}
闭包
闭包就是能够读取其他函数内部变量的函数。
可以理解为: 定义在一个函数内部的函数
本质上:闭包将函数内部和函数外部连接起来的桥梁
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 |
it指代当前对象 | 闭包形式返回 | 是 | 适用于处理不为null的操作场景 |
with | fun |
this指代当前对象或者省略 | 闭包形式返回 | 否 | 适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可,经常用于Android中RecyclerView中onBinderViewHolder中,数据model的属性映射到UI上 |
run | fun |
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中一样,进行手动判断。
数字转换
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
协变
如果 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> 只能添加,读取受限 | 实现方式:, 可读可写 |