Kotlin笔记三

Kotlin生成.class放入java编译器
Kotlin 开发效率提高50%
Kotlin 完美兼容java
Kotlin 几乎解决了空指针问题

《第一行代码第三版》Kotlin


(十五)高阶函数

接收Lambda参数的函数就可以被称为具有函数式编程风格的API,而如果你想要定义自己的函数式API,就要借助高阶函数来实现了。

定义:如果一个函数接收另一个函数作为参数,或者返回值的类型是另一个函数,那么该函数就称为高阶函数。

Kotlin添加了函数类型的概念,使得能够传入一个函数,还能够返回一个函数。如果将函数类型声明添加到函数内部,那么这就是一个高阶函数。

函数类型的声明规则
(String,Int)-> Unit
// ->左边的部分就是用来声明该函数接收什么参数,如果不接受就写空括号
// ->右边的部分就是用来声明该函数的返回值是什么类型,如果没有返回值就用Unit
fun myFunExample(func: (String, Int) -> Unit) {
        func("hello", 123)
}

高阶函数的作用

允许让函数类型的参数来决定函数的执行逻辑。即使是同一个高阶函数,只要传入不同的函数类型参数,那么它执行逻辑和最终的返回结果就可能完全不同。

fun num1Andnum2(num1: Int, num2: Int, func: (Int, Int) -> Int): Int {
    //在函数里面,没有进行任何具体的运算,而是将num1和num2参数传给了第三个函数类型参数,并获取它的返回值
    //最终把得到的返回值进行返回
    val result = func(num1, num2)
    return result
}

调用高阶函数

一、函数引用的方式(::函数名)

fun num1Andnum2(num1: Int, num2: Int, func: (Int, Int) -> Int): Int {
    //在函数里面,没有进行任何具体的运算,
    //而是将num1和num2参数传给了第三个函数类型参数,并获取它的返回值
    val result = func(num1, num2)
    return result
}

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

fun minus(num1: Int, num2: Int): Int {
    return num1 - num2
}

fun main() {
    val num1 = 100
    val num2 = 80
    val result1 = num1Andnum2(num1, num2, ::plus)
    val result2 = num1Andnum2(num1, num2, ::minus)
}


二、Lambda表达式方式:

fun StringBuilder.my_build(my_func: StringBuilder.() -> Unit): StringBuilder {
    //给StringBuilder类定义了一个my_build拓展函数
    //这个拓展函数接收一个函数类型参数,并且返回值类型也是StringBuilder
    //StringBuilder.的语法结构是(ClassName.),它表示这个函数类型定义在哪个类当中
    //好处是当我们调用build函数时传入的lambda表达式将会自动拥有StringBuilder的上下文
    my_func()
    return this
}


高阶函数的实现原理
被编译成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 num1 = 100;
      int num2 = 80;
      int result = num1Andnum2(num1,num2,new Function(){
      @Override
      public Integer invoke(Integer n1, Integer n2){
      return n1+n2;
}
})
}

内联函数

Kotlin提供了内联函数的功能,它可以将使用Lambda表达式带来的运行时开销完全消除;
只需要在定义高阶函数时加上inline关键字

inline fun StringBuilder.my_build(my_func: StringBuilder.() -> Unit): StringBuilder {
    my_func()
    return this
}

noinline与crossinline

问题:一个高阶函数如果接收了两个或者更多函数类型的参数,我们加上inline,那么就会自动将所有引用的lambda都进行内联。

>如果我们只想内联其中一个的话,就在不想内联的参数前面加上noinline
inline fun inlineTest(block1: () -> Unit, noinline block2: () -> Unit) {
    //这样就只会对block1进行内联
}
为什么需要非内联?
1)内联的函数类型参数只允许传递给另外一个内联函数;
2)同时内联函数所引用的Lambda表达式中是可以使用return关键字来进行函数返回的,而非内联函数只能进行局部返回;
3)将高阶函数声明成内联函数是一种良好的编程习惯

>crossinline 能够保证在内联函数的lambda表达式中一定不会使用return 关键字,声明了crossinline以后,我们就不能在lambda表达式中使用return关键字进行返回了。

(十六)高阶函数的应用

高阶函数非常适用于简化各种API的调用,一些API的原有用法在使用高阶函数简化之后,不管是在易用性还是可读性方法,都可能会有很大的提升。

简化SharedPreferences的用法
//java的思维
        val editor = getSharedPreferences("data", Context.MODE_PRIVATE).edit()
        editor.putString("name", "Tim")
        editor.putInt("age", 28)
        editor.putBoolean("married", false)
        editor.apply()

高阶函数的简化:
先新建一个SharedPreferences.kt文件,里面写如下代码:
fun SharedPreferences.openSP(func: SharedPreferences.Editor.() -> Unit) {
    //openSP函数是一个高阶函数
    val editor = edit()
    editor.func()
    editor.apply()
}
然后在项目里面这样子进行调用
getSharedPreferences("data", Context.MODE_PRIVATE).openSP {
            //我们可以直接在SharedPreferences对象上调用open函数,
            // 然后在Lambda表达式中完成数据的添加操作
            putString("name", "Tim")
            putInt("age", 28)
            putBoolean("married", false)
        }


//在core-ktx库里面Google给了我们类似openSP的方法,叫做edit{ }

简化ContentValues的用法
//java的思维
        val values = ContentValues()
        values.put("name", "Game of T")
        values.put("pages", 720)
        values.put("price", 20.88)
        db.insert("Book", null, values)

高阶函数的简化:
先建一个ContentValues,kt文件,写一个高阶函数继承ContentValues:
fun cvOf(vararg pairs: Pair<String, Any?>): ContentValues {
    //vararg关键字就是java的可变参数列表,这样就允许传入0个,1个,2个Pair类型的
    //Pair类型是一种键值对的数据结构,因此需要通过泛型来指定它的键值分别对应什么类型的数据
    val cv = ContentValues()
    for (p in pairs) {
        val key = p.first
        val value = p.second
        when (value) {
            is Int -> cv.put(key, value)
            is Long -> cv.put(key, value)
            is Short -> cv.put(key, value)
            is Float -> cv.put(key, value)
            is Double -> cv.put(key, value)
            is Boolean -> cv.put(key, value)
            is String -> cv.put(key, value)
            is Byte -> cv.put(key, value)
            is ByteArray -> cv.put(key, value)
            null -> cv.putNull(key)
        }
    }
    return cv
}
然后在项目里面这样子进行调用
        val values = cvOf(
            "name" to "Game of T",
            "pages" to 720,
            "price" to 20.88
        )
        db.insert("Book", null, values)


//在core-ktx库里面Google给了我们类似cvOf的方法,叫做contentValuesOf()方法

(十七)泛型

和java中相同的部分

泛型允许我们在不指定具体类型的情况下进行编程,这样编写出来的代码将会拥有更好的扩展性。

->定义泛型类:

class MyClass<T> {
    //将MyClass的泛型指定为T,
    //于是method方法可以接收一个T类型的参数,
    //并且它的返回值也是T类型
    fun method(param: T): T {
        return param
    }
}

//调用
val myclass = MyClass<Int>()
val res = myclass.method(123)

定义泛型方法:

class MyClass {
    //可以在调用method方法的时候指定类型
    fun <T> method(param: T): T {
        return param
    }
}

//调用
        val myclass = MyClass()
        val res = myclass.method<Int>(123)


->泛型上界:
可以通过指定上界的方式来对泛型的类型进行约束,默认情况下为可空类型,即Any?;设置为不空类型,就写Any;设置为数字类型就<T:Number>。
可以看下apply函数的源码:

public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}

和Java中不同的部分(第十章)

(十八)委托

委托是一种设计模式,它的基本理念是:操作对象自己不会去处理某段逻辑,而是会把工作委托给另外一个辅助对象去处理。

类委托

核心思想是将一个类的具体实现委托给另一个类去完成。类委托的意义是,如果我们只是让大部分实现调用辅助对象中的方法,少部分的方法实现由自己来写,甚至加入一些自己独有的方法,那么该类就会称为一个全新的数据结构,这就是委托模式的意义所在。

->使用by关键字

class MySet<T>(val helperSet: HashSet<T>) : Set<T> by helperSet {
    //定义一个MySet类,实现Set接口;
    //在构造函数里面接收了一个HashSet参数;
    //HashSet就是一个辅助对象,在Set接口所有的方法实现中都没由进行自己的实现,而是默认调用了辅助对象中相应方法的实现;
    //如果我们要对某个方法进行重新实现,只需要单独重写那一个方法就可以了;
    fun helloWorld() {
        print("hello world")
    }

    override fun isEmpty(): Boolean {
        return false
    }
}

委托属性

核心思想是将一个属性(字段)的具体实现委托给另一个类去完成。

->使用by关键字

我们让p属性的具体实现委托给了myDelegateClass类去完成,当调用p属性的时候会自动调用myDelegateClass类的getValue方法,当给p属性赋值的时候会自动调用myDelegateClass类的setValue方法。
class MyClass {
    var p by myDelegateClass()
}
class myDelegateClass {

    var propVal: Any? = null

    operator fun getValue(myClass: MyClass, prop: KProperty<*>): Any? {
        return propVal
    }

    operator fun setValue(myClass: MyClass, prop: KProperty<*>, value: Any?) {
        propVal = value
    }

}
//getValue()方法接收2个参数,第一个参数用来声明委托功能可以在什么类中使用,第二个参数KProperty<*>是一个属性操作类,可以用于获取各种属性相关的值。
//setValue()方法接收3个参数,前两个和getValue方法相同,最后一个参数表示具体要赋值给委托属性的值,这个参数的类型必须和getValue()方法返回值的类型保持一致。


(十九)infix函数

to不是Kotlin中的关键字,之所以我们能够使用A to B 这样的语法结构是因为Kotlin提供了一种高级语法糖特性:infix函数。这种语法更接近于使用英语的语法来编写程序。

例:我们用infix函数去提升一下String类的startsWith函数的语法可读性

infix fun String.beginsWith(prefix: String): Boolean {
    //我们借助infix函数来提高beginsWith函数的可读性。
    return startsWith(prefix)//内部直接调用startsWith函数
}

调用的时候就直接可以使用beginsWith
if ("Hello Kotlin" beginsWith "Hello") {
    //todo
}

有两个比较严格的限制

第一个是infix函数必须接收且只能接收一个参数
第二个是infix函数必须是某个类的成员函数(注意:拓展函数就是在某个类里面的)

看一下to函数的源码
public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值