美文网首页
Kotlin学习---高阶

Kotlin学习---高阶

作者: 初夏的雪 | 来源:发表于2024-03-13 15:30 被阅读0次

    上一节我们学习了Kotlin的一些基础语法包括和java语言的一些区别,虽然说java和kotlin是无缝对接,但是他们彼此之间相互调用还有一些需要注意的地方,

    一、java与Kotlin互相调用

    1、kotlin调用java时,需要做非空的判断处理,这样避免出现空指针异常

    2、kotlin调用java的接口,可以使用(object : javaCallback )进行实例化后实现接口的方法

                                        可以直接使用({这里显示接口方法的函数体})
    
                                          直接创建接口的的实例,并实现接口
    

    3、java调用kotlin的方法,可以通过文件的名.方法名来进行调用,但是如果不想让java调用,可以使用双单引号将方法名字包含起来就无法调用了

    fun 'show'(){
        //此方法无法被java调用
    }
    

    4、kotlin中的扩展函数(就是可以在任何地方为一个类扩展一个函数)

    class student{
    }
    
    //此时为student添加了一个扩展函数
    fun student.add(var number:Int,var number2:Int){
    }
    
    fun main():Int{
    student.add(1,2);
    }
    

    5、kotlin泛型

    1)java中泛型的通配符是? 而Kotlin泛型的通配符是 *

    2)kotlin 中的out 相当于java是 extends 只读 输出

                    in   相当于java是 super    只写   输入
    
    open class fuclass {
        var name1 = "fuclass"
    }
    
    class ziclass : fuclass() {
        var name = "ziclass"
    }
    fun main() {
    
        /**
         * TODO in out 的读写模式
         */
        var list: MutableList<out fuclass> = ArrayList<ziclass>()
        var temp = ziclass()
    //    list.add(temp)//不能修改  报错
        var item1 = list.get(0)//获取
    
        var list2: MutableList<in ziclass> = ArrayList<fuclass>()
    //    list2.add(fuclass())
        var item = list2[0]
        println("name=${(item as ziclass).name},name1=${item.name1}")
    
    }
    

    3) in out 在类的声明泛型时作用:可以控制整个类泛型读写模式, 而java中不能在泛型声明的时候限定泛型的读写模式

      n 说明该类的泛型只能写     相当于输入,就是只写的
    
      out 说明该类的泛型只能获取  相当于输出,就是只读的
    
      不使用in out 说明该类的泛型既可以输入,也可以输出
    
    //在声明泛型时候的泛型读写模式的限定
    class Student<in T> {
        //可写
        fun setData(data: T) {
        }
        //不可读,报错
        fun getData(): T?{
            return null
        }
    }
    
    class Teacher<out T> {
        //不可写,报错
    //    fun setData(data: T) {
    //    }
    
        //可读
        fun getData(): T? {
            return null
        }
    }
    

    二、高阶函数

    概念:将函数作为参数传递给主函数,该主函数称为高阶函数。

    高阶函数在kotlin中随处可见,想要深刻理解高阶函数,需要先来学习一下lambda表达式,这样就让你学起高阶函数了达到事半功倍的效果。

    2.1)lambda表达式

    1、:() 括号中是参数

    2、={} 括号中是函数体

    3、var 变成val 后就不可以覆盖了

    4、只有一个参数时可以不写(默认是it),多个参数则需要写清楚

    //() 参数为空,返回值:Unit   函数名method , 该函数不能调用,因为没有函数体
    var method:()->Unit
    
    //有函数体,可以调用
    var method1:(Int ,Int)->Int={number1+number2=number1+number2}
    method1(9,9)
    
    var method3={number1:Int,number2:Int-> number1+number2}
    
    //只有一个参数
    var m10 : (Int) -> Unit = {
            when(it) {
                1 -> println("你是一")
                in 20..30 -> println("你是 二十 到 三十")
                else -> println("其他的数字")
            }
        }
        m10(29)
    
    //无参数
     var m12 = { println("我就是m12函数,我就是我") }
        m12()
    
    
     // 覆盖操作
      var m14 = {number: Int -> println("我就是m14  我的值: $number")}
        m14 = {println("覆盖  我的值: $it")}
        m14(99)
    

    2.2)高阶:

    1、高阶函数的最后一个参数是一个函数时,在实现函数体时 可以在函数参数括号外使用{}来实现函数参数的函数体

    2、使用typealias 来给高阶函数起一个别名 typealias LOGIN= (String, String) -> Unit 他就像一个数据类型一样的使用

    3、当函数参数只有一个参数时,可以直接使用{}进行闭包,默认将一个参数命名为it,
    有多个参数时,无法默认 则需要指明参数名,并用->指向函数体

    4、 如果函数参数已经有了实现体,那么可以通过:: 来直接调用;

    :: 内部原理:就是将show_run函数的对象赋值给show_14的参数,那么说明show_run 是可以赋值给一个变量的

    下面是typealias 的使用:

    //类似于typedef
    typealias LOGIN = (String, String) -> Unit
    
    fun main(){
          fun loginService(username: String, password: String, login: (String, String) -> Unit) {
            login(username, password)
        }
        
        
         //高级别名的使用
        fun loginService2(username: String, password: String, login: LOGIN) {
            login(username, password)
        }
        
        //真正使用
        fun loginEngine(username: String, password: String): Unit {
            loginService(username, password) { username, pwd ->
                //此处就是高阶函数的函数体
                println("username=${username},pwd=${password}")
            }
        }
         loginEngine("leon", "123456")
    }
    
    

    下面是一个或多个参数的使用:

    fun main(){
     //  多个参数的函数参数
        fun show(mm: (String) -> Unit) {
            mm("一个参数")
        }
    
        show {
            println("一个参数的函数参数:$it")
        }
    
        fun show1(mm: (String, Int) -> Unit) {
            mm("多个个参数", 1)
        }
        //下面是调用
        show1 { param1, param2 ->
            println("多个参数的函数参数:param1=$param1,param2=$param2")
        }
    }
    

    下面是::来调用

    fun main(){
     // :: 来调用高阶的函数参数
    
        fun show_14(number: Int, mm: (Int) -> String): String {
            return mm(number)
        }
    
        fun show_run(number: Int) = "通过:: 来调用函数参数的实现函数_$number"
    
        //
        val showRunObj = ::show_run
        println(show_14(50, showRunObj))
        println(show_14(100, ::show_run))
    }
    

    2.3) 高阶里面的手写标准

    1、kotlin中的泛型除了数据类型,还包含了方法,方法也算一个类型,即万能类型

    2、T 是泛型,R是返回类型的泛型

    3、T.MyRun相当于给泛型T增加了一个MyRun的扩展函数, mm: T.() -> R 代表给T增加一个匿名的扩展函数

    4、类似于RxJava中的链式调用

    fun main(){
    fun <T, R> T.MyRun(mm: () -> R): R {
            return mm()
        }
    
        //给T增加一个匿名函数 T.()
        fun <T, R> myWith(input: T, mm: T.() -> R): R {
            return input.mm()
        }
    
        var age = 0
        age.MyRun {
            "headworld"
        }
    
        var name = "Leon"
        myWith(name) {
            length//此时可以直接获取name的长度
        }
    }
    

    2.4 高阶里面的标准特色:let、apply 、also、run、with、takeIf、takeUnless、repeat

    kotlin给我们提供了很多类似的标准,可以参考Standard.kt里面的自己去学习,

    1. let : 其实就是将调用类型的对象自己作为参数传递给block的函数体

    2. apply: 其实就是在执行完block函数后,将apply的调用者返回,类似于建造者模式的链式调用

    3. also:将当前调用者作为参数传递给block函数,并将调用者作为返回值返回,便于链式调用

    4. run: 将调用者作为参数传递给block函数,并将block执行的结果返回

    5)with:使用指定的receiver作为接受者调用block函数后,将block函数执行的结果返回

    6)takeIf:当调用者满足predicate的条件时返回调用者,不满足时返回null

     takeUnless: 和takeIf正好相反
    

    7)repeat: 其实就是系统提供出来的一个轮询器,方便我们遍历数组

        var Leon = "Leon"
        Leon.let {
            println("let 参数it的值是${it}")
        }
    
        var temp = Leon.apply {
            println("apply 回传的this的值是$this")
        }
        println("apply 执行后的结果$temp")
    
        repeat(Leon.length) {
            println("repeat轮询:it=${it},leon的当前位置的字符是:${Leon[it]}")
        }
    
    //输出的结果如下:
    //let 参数it的值是Leon
    //apply 回传的this的值是Leon
    //apply 执行后的结果Leon
    //repeat轮询:it=0,leon的当前位置的字符是:L
    //repeat轮询:it=1,leon的当前位置的字符是:e
    //repeat轮询:it=2,leon的当前位置的字符是:o
    //repeat轮询:it=3,leon的当前位置的字符是:n
    

    虽然kotlin给我们提供了很多标准,其实我们还是可以自己定义我们所需要的的标准的,下面来模仿一下系统的repeat 做一个自定义

    说明:

    1)我们在使用下标的时候,下标从0 开始,所有这里在执行轮询的时候,需要使用区间,但是必须until 去掉尾部,否则会多执行一次

    2)step 后面的步长,必须要明确说明,不能作为参数来进行赋值

    3)真正的操作是在action这个函数参数的函数体内完成

     fun main(){
     
     fun doWhile(count: Int, action: (Int) -> Unit) {
            for (index in 0 until count step 1) {
                action(index)
            }
        }
    
        doWhile(10) {
            println("自定义轮询器的下标:$it")
        }
    }
    
    
    //输出结果如下:
    自定义轮询器的下标:0
    自定义轮询器的下标:1
    自定义轮询器的下标:2
    自定义轮询器的下标:3
    自定义轮询器的下标:4
    自定义轮询器的下标:5
    自定义轮询器的下标:6
    自定义轮询器的下标:7
    自定义轮询器的下标:8
    自定义轮询器的下标:9
    

    2.5)自定义线程的封装

    1)我们分封装的线程,这样后续我们可以直接值关心耗时操作即可,

    2) openThread 可以作为一个模板来使用

     fun openThread(start: Boolean, RunTask: () -> Unit): Thread? {
            val thread = object : Thread() {
                override fun run() {
                    super.run()
                    RunTask()
                }
            }
            return if (start) {
                thread.start()
                thread
            } else {
                null
            }
        }
        
        fun main(){
           //对比系统的thread代码,大体类似
    
        openThread(true) {
            println("我是在子线程中执行的耗时操作:输出字符串")
        }
        }
    

    今天我们主要学习了一下kotlin和java互相调用时一些需要注意的点,着重学习了kotlin中的高阶和自定义标准模板,希望对各位看官有所帮助。

    相关文章

      网友评论

          本文标题:Kotlin学习---高阶

          本文链接:https://www.haomeiwen.com/subject/xznbyltx.html