kotlin基础--匿名函数、闭包

2021-12-06 17:16:32 浏览数 (1)

上篇介绍了kotlin的基本类型,变量定义、函数定义等,接下来来学习如何使用匿名函数、lambda、闭包,这将大大提高我们使用kotlin编写代码的效率,相对于Java繁琐的代码,你会爱上这种简洁
一.匿名函数

Java也有匿名函数,但是kotlin比Java简洁很多

1.函数变量

如果我们要在Java方法中传入一个回调函数,需要定义一个接口,并使用new关键字实例化匿名类实现该方法:

代码语言:javascript复制
    public static void main(String[] args) {
        //调用方法,并实例化匿名类实现接口
        registerConnectCallback(new ConnectCallback() {
            @Override
            public void connectCallback(boolean isConnect) {
                
            }
        });
    }
    
    //定义接口
    interface ConnectCallback{
        void connectCallback(boolean isConnect);
    }
    
    //定义方法 入参为接口
    private static void registerConnectCallback(ConnectCallback connectCallback){
        connectCallback.connectCallback(true);
    }

再来看看kotlin是如何实例化匿名函数的:

代码语言:javascript复制
fun main() {
    var count = "aabbccddaa".count({ letter ->
        letter == 'a'
    })
    print(count)
}

结果:

4

{}内代表了实例化的匿名函数

2.函数类型和隐式返回

kotlin中函数可以直接赋值给变量,类似于c/c 中的函数指针,而Java我们需要定义接口

代码语言:javascript复制
fun main() {
    //  变量名 :()代表这是个函数类型变量 -> String代表函数的返回值
    val funcp: () -> String = {
        "我是一个函数类型变量"
    }

    println(funcp)
    println(funcp())
}

结果:

代码语言:javascript复制
Function0<java.lang.String>
我是一个函数类型变量

匿名函数在绝大数情况下,不需要return关键字,默认会使用最后一行代码作为返回值

3.匿名函数入参

我们看1.内置函数的时候,肯定会看不懂kotlin的写法:

代码语言:javascript复制
{ letter ->
        letter == 'a'
}

其实符号->之前的就是代表了入参,不过这种写法是简化过的,完整的写法如下:

代码语言:javascript复制
fun main() {
    //  ()内表示入参参数类型             //->前代表入参,并赋予了变量名
    val funcp: (String) -> String = { name: String ->
        "我是一个函数类型变量:$name"
    }

    println(funcp("张三的函数"))
}
4.it关键字

匿名函数只有一个入参时,可以省略参数名,使用it关键字

代码语言:javascript复制
fun main() {
    //  ()内表示入参参数类型
    val funcp: (String) -> String = {
        "我是一个函数类型变量:$it"
    }

    println(funcp("张三的函数,这是it变量"))
}
5.类型推断

和变量的类型推断相同,当初始化时就赋值一个匿名函数,并且没有入参,那么变量就不需要指定类型

代码语言:javascript复制
fun main() {
    val funcp = {
        "我是一个匿名函数"
    }

    println(funcp())
}
6.入参类型推断

如果匿名函数有入参,那么参数名和入参类型必须有,但是参数名后面不需要指定参数类型了

以3.匿名函数入参中为例子:

代码语言:javascript复制
fun main() {
    //  ()内表示入参参数类型             //->前代表入参,并赋予了变量名
    val funcp: (String) -> String = { name ->
        "我是一个函数类型变量:$name"
    }

    println(funcp("张三的函数"))
}

用过lambda的都发现,我们匿名函数其实就是lambda表达式的写法,这种写法大大的精简了代码

二.函数作为参数
1.函数变量作为入参

Java中使用的接口作为入参,而kotlin可以直接传递函数变量

代码语言:javascript复制
fun main() {
    //定义匿名函数变量
    val performCalc: (Int, Int) -> String = { a, b ->
        val sum = a   b
        "$sum"
    }

    //调用其他函数,传入函数变量
    printCalcResult(3, 4, performCalc)
}

fun printCalcResult(a: Int, b: Int, funcp: (Int, Int) -> String) {
    //调用匿名函数执行a b
    println(funcp(a, b))
}
2.函数作为入参

我们也可以直接传递一个匿名函数

代码语言:javascript复制
fun main() {
    //调用其他函数,传入函数变量
    printCalcResult(3, 4, { a, b ->
        val sum = a   b
        "$sum"
    })
}

fun printCalcResult(a: Int, b: Int, funcp: (Int, Int) -> String) {
    //调用匿名函数执行a b
    println(funcp(a, b))
}

如果匿名函数排在入参的最后,或者匿名函数是唯一参数,那么圆括号可以省略

2.1有其他参数的:

代码语言:javascript复制
fun main() {
    //调用其他函数,传入函数变量
    printCalcResult(3, 4) { a, b ->
        val sum = a   b
        "$sum"
    }
}

fun printCalcResult(a: Int, b: Int, funcp: (Int, Int) -> String) {
    //调用匿名函数执行a b
    println(funcp(a, b))
}

2.2只有一个函数参数:

代码语言:javascript复制
fun main() {
    //调用其他函数,传入函数变量
    printCalcResult { a, b ->
        val sum = a   b
        "$sum"
    }
}

fun printCalcResult(funcp: (Int, Int) -> String) {
    //调用匿名函数执行a b
    println(funcp(3, 4))
}
3.函数内联

在JVM上,定义的lambda会以实例化对象存在,虚拟机会为此分配内存,为了解决这种额外的内存开销,kotlin有一种优化机制叫"内联",内联实际上就是在编译时会把代码复制一份替换lambda

使用方法:inline关键字

代码语言:javascript复制
inline fun printCalcResult(funcp: (Int, Int) -> String) {
    //调用匿名函数执行a b
    println(funcp(3, 4))
}

递归的函数不能使用内联,由于会无限复制,编译时会报错

4.具名函数引用

除了匿名函数外,我们还可以将具名函数作为参数传递

代码语言:javascript复制
fun main() {
    //将具名函数calc作为参数传递
    printCalc(3, 4, ::calc)
}

//具名函数calc
fun calc(a: Int, b: Int): Int {
    return a   b
}

inline fun printCalc(a: Int, b: Int, p: (Int, Int) -> Int) {
    //调用具名函数变量p方法
    println(p(a, b))
}
5.返回参数为函数

函数也可以作为函数的返回值

代码语言:javascript复制
fun main() {
    val p = printCalc()
    println(p())
}

//返回类型为 () -> Int 的函数
inline fun printCalc(): () -> Int {
    //返回一个匿名函数
    return {
        val a = 3
        val b = 4
        a   b
    }
}
三.闭包

在kotlin中匿名函数可以修改和引用在自己作用域外的变量,而Java要做到这一点,只能用final关键字修饰一个引用型变量,匿名函数引用着定义自己的函数中的变量,kotlin中lambda就是闭包

能接受函数或返回函数的函数叫作高级函数,它们广泛运用于函数式编程中

代码语言:javascript复制
fun main() {
    val p = printCalc()
    println(p())
}

//返回类型为 () -> Int 的函数
inline fun printCalc(): () -> Int {
    var a = 3
    val b = 4
    //返回一个匿名函数
    return {
        //修改作用域外的变量
        a  
        a   b
    }
}

0 人点赞