一 🌻高阶函数代替回调
1.1 无参示例
fun main() {
pay(Runnable {
println("回调函数..")
})
pay{
println("高阶函数..")
}
}
fun pay(block: Runnable) {
println("before block")
block.run()
println("end block")
}
fun pay(block: () -> Unit) {
println("before block")
block()
println("end block")
}
输出的结果:
before block
回调函数
end block
before block
高阶函数
end block
这看不出啥区别,当需求发生变化,我想在回调方法中加个String
类型的参数,该如何实现呢?
1.2 带参示例
// 定义接口
interface Consumer {
fun accept(way: String)
}
fun main() {
pay(Consumer {
println("回调函数..it=$it")
})
pay{
println("高阶函数..it=$it")
}
}
fun pay(block: Consumer) {
println("before block")
block.accept("支付宝")
println("end block")
}
// String 指的是方法参数
fun pay(block: (String) -> Unit) {
println("before block")
block("支付宝")
println("end block")
}
// Int 指的是block函数的返回值
fun pay2(block: (String) -> Int) {
println("before block")
block("支付宝")
println("end block")
}
- 如果是以回调的形式,得先创建一个接口,定义参数,如果后面参数变成2个,又得新建1个接口。
- 而
kt的高阶函数
用起来更加灵活。
二 🌵内联函数
2.1 介绍
- 定义:方法名前面 加个
inline
就是内联函数 - 作用:减少函数的调用来优化性能
- 使用场景:并不是每个函数前加一个 inline 就可以优化性能,如果某个方法的参数包含
高阶函数
,那建议加上inline。典型的一个应用场景就是Kotlin的集合类。
filter
和 map
方法里的参数都是高阶函数,所以这2个方法加上 inline
关键字后,调用这2个方法时,会减少嵌套, 优化性能,见下面示例。
2.2 非内联函数
fun main() {
pay(Runnable {
println("回调函数..")
})
payNoInline{
println("高阶函数..")
}
}
fun pay(block: Runnable) {
println("before block")
block.run()
println("end block")
}
fun payNoInline(block: () -> Unit) {
println("before block")
block()
println("end block")
}
通过字节码反编译的相关Java代码如下:
- 发现高阶函数作为参数时和方法回调没什么区别,
- 并且方法参数中还新增加了额外的类
Function0
和block.invoke()
函数调用开销。
2.3 🔥内联函数
fun main() {
pay(Runnable {
println("回调函数..")
})
payInline{
println("高阶函数..")
}
}
...
fun payInline(block: () -> Unit) {
println("before block")
block()
println("end block")
}
通过字节码反编译的相关Java代码如下:
-
当调用 payInline( 高阶函数) 时,会把 payInline()里面的方法体取过来并和当前高阶函数合成一个整体执行。
-
不会调用 payInline()方法,不会调用block.invoke(),执行效率提高了许多。
-
当函数里的某个参数为高阶函数时,建议您使用
inline
修饰该函数。
三 🌷泛型
3.1 🔥reified 强烈推荐
- reified 是kt语言在泛型中独有的关键字,作用是把抽象的东西更加具体或真实,让泛型用起来更简单安全。
inline fun <reified T : Activity> Activity.openAct() {
startActivity(Intent(this, T::class.java))
}
fun <T : Activity> Activity.openAct(clazz: Class<T>) {
startActivity(Intent(this, clazz))
}
fun main(){
// 启动Activity的方式1
openAct(MyAct::class.java)
// 启动Activity的方式2
openAct<MyAct>()
}
使用 reified
时,必须和inline
一起。再见 .class.java
,你好 reified
3.2 上界约束(out or extend)
- java:
T extends Object
- kotlin:
T : Object
和out : Object
指的是其类型必须是它的子类型或者它自己。out
一般将泛型作为某个对象返回, 详情见下面 out
协变讲解。
类的关系图如下:
open class People
open class Man : People()
class Man1 : Man()
class Man2 : Man()
class User<T : Man>
fun <T> createUser(user4: User4<out Man>){}
fun main()
var u = User<Man>() // 泛型可以是自己
var u1 = User<Man1>() // 泛型可以是子类
// var u2 = User<People>() // 编译错误
val c1 = createUser(User4(Man1()))
// val c2 = createUser(User4(People())) // 编译错误
}
3.3 下界约束(in or super)
- java中是
? super Object
- kotlin中是
in Object
open class People
open class Man : People()
class Man1 : Man()
class Man2 : Man()
class User<T : Man>
fun main() {
var list: ArrayList<in Man>?= ArrayList()
list.add(Man())
// 为什么可以添加进去呢?因为编译器会这么去存list.add((Man)Man1)
// 小转大是隐性的,大转小强转才需要手动加类型。
list.add(Man1())
// 那这里为什么又不能存 Man 的父类呢?
// 因为手动add()进去的数据都必须是绝对安全的(最低级父类:本身)才能通过。所以直接add父类也是不行的。
// list.add(People()) 编译错误
fun getMan(): ArrayList<Man> = ArrayList()
fun getMan1(): ArrayList<Man1> = ArrayList()
fun getPeople(): ArrayList<People> = ArrayList()
list = getMan()
// list = getMan1() 编译错误
list = getPeople()
}
3.4 类型限定(where)
interface Fly
class Man
class Man1 : Man, Fly
class Man2 : Man
// 限定传入的T只能是Man或其子类 并且实现了Fly接口
class User<T>(var t:T) where T: Man, T:Fly
fun main() {
var u1 = User(Man1())
// var u2 = User(Man2()) // 编译报错,Man2没有实现Fly
}
3.5 变
3.5.1 out (协变)
如果你的类是将泛型作为内部方法的返回,那么可以用 out:
interface Creation<out T> {
fun create(): T
}
create 创建指定泛型对象。因此,可以这样来记:create = output = out。
3.5.2 in(逆变)
如果你的类是将泛型对象作为函数的参数,那么可以用 in:
interface Consumer<in T> {
fun consume(item: T)
}
consume 消费指定泛型对象。因此,可以这样来记:consume = input = in。
3.5.3 Invariant(不变)
如果既将泛型作为函数参数,又将泛型作为函数的输出,那就既不用 in 或 out。
interface CreationConsumer<T> {
fun create(): T
fun consume(item: T)
}
五 for 6种循环
// 带索引遍历
for ((index, item) in arrayListOf("a", "b", "c").withIndex()) {
println("$index - $item")// 0-a 1-b 2-c
}
// 包左包右
for (i in 1..10) {
println(i) // 12345678910
}
// 包左不包右
for (i in 1 until 10) {
println(i) // 123456789
}
// 降序-包左包右
for (i in 10 downTo 1) {
println(i) // 10987654321
}
// 跳步-包左包右
for (i in 1..10 step 2) {
println(i) // 13579
}
// 包左不包右
repeat(10) {
print(it) // 0123456789
}