Kotlin学习---高阶

2024-03-13  本文已影响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中的高阶和自定义标准模板,希望对各位看官有所帮助。

上一篇下一篇

猜你喜欢

热点阅读