kotlin基础

本文介绍了Kotlin中的高阶函数概念,如定义、应用(如简化SharedPreferences和ContentValues操作),以及内联函数的使用,以减少运行时开销。同时提到了KTX库如何简化数据库操作。
摘要由CSDN通过智能技术生成

五、高阶函数
1. 定义高阶函数
如果一个函数接收另一个函数作为参数,或者返回值的类型是另一个函数,那么该函数就称为高阶函数。
函数类型的定义:
(Sting, Int) -> Unit
现在将上述函数类型添加到某个函数的参数声明或者返回值声明上,那么这个函数就是一个高阶函数

fun example(func: (String, Int) -> Unit) {
    func("hello", 123)
}
1
2
3
高阶函数允许让函数类型的参数来决定函数的执行结果。
定义一个叫做num1AndNum2()的高阶函数,并让它接收两个整型和一个函数类型的参数,在函数中对传入的两个整型参数进行某种运算,并返回最终的运算结果,但是具体进行什么运算由传入的函数类型参数决定。

fun num1AndNum2(num1: Int, num2: Int, operator: (Int, Int) -> Int) : Int {
    return operator(num1, num2)
}

fun plus(num1: Int, num2: Int) : Int {
    return num1 + num2
}

fun minus(num1: Int, num2: Int) : Int {
    return num1 - num2
}
1
2
3
4
5
6
7
8
9
10
11
接下来调用

fun main() {
    val num1 = 100
    val num2 = 80
    val result1 = num1AndNum2(num1, num2, ::plus)
    val result2 = num1AndNum2(num1, num2, ::minus)
    println("result is $result1")
    println("result2 is $result2")
}
1
2
3
4
5
6
7
8
这里::plus和::minus是一种函数引用方式的写法。但是每次调用任何高阶函数的时候都还得先定义一个与其函数类型参数相匹配的函数,过于复杂。
Kotlin还支持其他多种方式来调用高阶函数,比如Lambda表达式、匿名函数、成员引用。
将上述代码改成Lambda表达式的写法

fun main() {
    val num1 = 100
    val num2 = 80
    val result1 = num1AndNum2(num1, num2) {n1, n2 ->
        n1 + n2
    }
    val result2 = num1AndNum2(num1, num2) {n1, n2 ->
        n1 - n2
    }
    println("result is $result1")
    println("result2 is $result2")
}
1
2
3
4
5
6
7
8
9
10
11
12
接下来根据之前学习的apply函数,使用高阶函数模仿实现类似的功能,给StringBuilder加一个扩展函数

fun StringBuilder.build(block: StringBuilder.() -> Unit) : StringBuilder {
    block()
    return this
}
1
2
3
4
这里函数类型参数的声明方式和之前学的语法不同,它在函数类型前面加上了一个StringBuilder.的语法结构,这是这才是定义高阶函数完整的语法规则,在函数类型前面加上ClassName.就表示这个函数类型是定义在哪个类当中的。
这里函数类型定义在StringBuilder类中,当我们调用build函数时传入的Lambda表达式将会自动拥有StringBuilder的上下文。
此时的build函数只能作用在StringBuilder类上面,而apply函数是可以作用在所有类上面的,如果想实现apply函数这个功能,需要借助Kotlin的泛型才行。

2. 内联函数的作用
fun num1AndNum2(num1: Int, num2: Int, operator: (Int, Int) -> Int) : Int {
    return operator(num1, num2)
}

fun main() {
    val num1 = 100
    val num2 = 80
    val result1 = num1AndNum2(num1, num2) {n1, n2 ->
        n1 + n2
    }
}
1
2
3
4
5
6
7
8
9
10
11
Kotlin代码最终还是要编译成Java字节码的,但Java中并没有高阶函数的概念。
Kotlin的编译器会将这些高阶函数的语法转换成Java支持的语法结构,上述代码大致会被转换成如下Java代码

public static int num1AndNum2 (int num1, int num2, Function operation) {
    int result =(int) operation.invoke (num1, num2);
    return result;
}
public static void main() {
    int numl = 100;
    int num2 = 80;
    int result = num1AndNum2 (num1, num2, new Function() {
        @Override
        public Integer invoke (Integer n1, Integer n2) {
            return n1 + n2;
        }
    });
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
可以看到第三个参数变成了一个Function接口,这是一种Kotlin内置的接口,里面有一个待实现的invoke()函数,而num1AndNum2()函数其实就是调用了Function接口的invoke()函数。
在调用num1AndNum2()函数的时候,之前的Lambda表达式在这里变成了Function接口的匿名类实现,然后在invoke()函数中实现了n1 + n2的逻辑。
这就时Kotlin高阶函数背后的实现原理。我们一直调用的Lambda表达式在底层被转换成了匿名类的实现方式。即我们调用一次Lambda表达式,都会创建一个新的匿名类实例,当然也会造成额外的内存和性能开销。
为了解决这个问题,Kotlin提供了内联函数的功能,它可以将使用Lambda表达式带来的运行时开销完全消除。
内联函数的用法:在定义高阶函数时加上inline关键字的声明

inline fun num1AndNum2(num1: Int, num2: Int, operator: (Int, Int) -> Int) : Int {
    return operator(num1, num2)
}
1
2
3
内联函数的原理就是Kotlin编译器会将内联函数中的代码在编译的时候自动替换到调用它的地方,这样就不存在运行时的开销了。
首先,Kotlin编译器会将Lambda表达式中的代码替换到函数类型参数调用的地方,然后再将内联函数中的全部代码替换到函数调用的地方。

3. noinline与crossinline
4. 高阶函数的应用
4.1 简化SharedPreferences的用法
val editor = getSharedPreferences("data", Context.MODE_PRIVATE).edit()
        editor.putString("name", "Tom")
        editor.putInt("age", 28)
        editor.putBoolean("married", false)
        editor.apply()
1
2
3
4
5
使用高阶函数简化SharedPreferences的用法

fun SharedPreferences.open(block: SharedPreferences.Editor.() -> Unit) {
        val editor = edit()
        editor.block()
        editor.apply()
    }
1
2
3
4
5
首先,通过扩展函数的方式向SharedPreferences类中添加了一个open函数,并且它还接收一个函数类型的参数,因此open函数是一个高阶函数
由于open函数内拥有SharedPreferences的上下文,所以直接调用edit()方法来获取SharedPreferences.Editor对象。
另外open函数接收的是一个SharedPreferences.Editor的函数类型参数,因此这里需要调用editor.block()对函数类型参数进行调用,就可以在函数类型参数的具体实现中添加数据了。
最后还要调用editor.apply()方法来提交数据,完成数据存储操作。
项目中使用如下

getSharedPreferences("data", Context.MODE_PRIVATE).open {
            putString("name", "Tom")
            putInt("age", 28)
            putBoolean("married", false)
        }
1
2
3
4
5
Google提供的KTX扩展库中已经包含了上述SharedPreferences的简化用法,这个扩展库会在Android Studio创建项目的时候自动引入build.gradle的dependencies中,即implementation ‘androidx.core:core-ktx:1.0.2’
因此,在项目中可以直接使用如下方法向SharedPreferences存储数据:

getSharedPreferences("data", Context.MODE_PRIVATE).edit {
            putString("name", "Tom")
            putInt("age", 28)
            putBoolean("married", false)
        }
1
2
3
4
5
edit函数就是Google的KTX库中自带的。

4.2 简化ContentValues的用法
val values = ContentValues()
        values.put("name", "Game of Thrones")
        values.put("author", "Sti")
        values.put("pages", "660")
        values.put("price", "21.2")
        db.insert("Book", null, values)
1
2
3
4
5
6
KTX库中提供了contentValueOf()方法

val values = contentValuesOf("name"  to "Game of Thrones", "author" to "Sti",
        "pages" to "660", "price" to "21.2")
    db.insert("Book", null, values)
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/bp1907/article/details/122333471

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值