当前位置:首页 » 《关注互联网》 » 正文

Kotlin 的语法和高级特性_让开,我要吃人了的博客

17 人参与  2021年08月28日 14:43  分类 : 《关注互联网》  评论

点击全文阅读


一 🌻高阶函数代替回调

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的集合类。

filtermap 方法里的参数都是高阶函数,所以这2个方法加上 inline 关键字后,调用这2个方法时,会减少嵌套, 优化性能,见下面示例。

image.png

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代码如下:

image.png

  • 发现高阶函数作为参数时和方法回调没什么区别,
  • 并且方法参数中还新增加了额外的类Function0block.invoke()函数调用开销。

2.3 🔥内联函数

fun main() {
   pay(Runnable {
       println("回调函数..")
   })
   payInline{
       println("高阶函数..")
   }
}
...
fun payInline(block: () -> Unit) {
   println("before block")
   block()
   println("end block")
}

通过字节码反编译的相关Java代码如下:

image.png

  • 当调用 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 : Objectout : Object

指的是其类型必须是它的子类型或者它自己。out一般将泛型作为某个对象返回, 详情见下面 out协变讲解。

类的关系图如下:

image.png

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
}


点击全文阅读


本文链接:http://zhangshiyu.com/post/26455.html

函数  高阶  回调  
<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1