美文网首页
Kotlin高级入门

Kotlin高级入门

作者: leeeyou | 来源:发表于2020-03-20 15:47 被阅读0次

    示例项目地址:https://github.com/Leeeyou/SampleOfKotlin-InDepth

    1. 操作符

    1.1 集合操作符

    元素相关的

    • + 、 - :往集合中增加或者删除某元素
    • groupBy:按照闭包条件分组
    • slice :按照入参将集合切分
    • take:从前往后拿取集合中的前n个元素,n是入参
    • takeLast:从后往前拿取集合中的前n个元素,n是入参
    • drop:从前往后丢弃集合中的前n个元素,n是入参
    • dropLast:从后往前丢弃集合中的前n个元素,n是入参
    • takeWhile:按照lambda表达式条件,从前往后拿取元素,直到第一个不符合条件的元素出现为止
    • takeLastWhile:按照lambda表达式条件,从后往前拿取元素,直到第一个不符合条件的元素出现为止
    • dropWhile:按照lambda表达式条件,从前往后丢弃元素,直到第一个不符合条件的元素出现为止
    • dropLastWhile:按照lambda表达式条件,从后往前丢弃元素,直到第一个不符合条件的元素出现为止
    • chunked:将集合按照n分块,n是入参,被分块的元素不在参与下一次分块
    • windowed:将集合按照n分块,n是入参,被分块的元素可能继续参与下一次分块,类似滑动窗口
    • zipWithNext:将集合两两一组切分
    • elementAt:返回对应的元素,越界会抛IndexOutOfBoundsException
    • first:返回符合条件的第一个元素,没有不返回任何内容
    • firstOrNull:返回符合条件的第一个元素,没有返回null
    • last:返回符合条件的最后一个元素,没有不返回任何内容
    • lastOrNull:返回符合条件的最后一个元素,没有返回null
    • elementAtOrNull:返回对应的元素,越界返回null
    • elementAtOrElse:返回对应的元素,越界则执行lambda表达式
    • find:同firstOrNull
    • findLast:同lastOrNull
    • contains:判断是否有指定元素
    • containsAll:判断是否包含指定的元素集

    排序相关的

    • sortedWith:接受一个Comparator对象
    • sorted:升序
    • sortedDescending:降序
    • sortedBy:自定义顺序排列
    • sortedByDescending:自定义逆序排列
    • asReversed:反序
    • shuffled:随机排序

    过滤相关的

    • filter:过滤掉所有满足条件的元素
    • filterNot:过滤所有不满足条件的元素
    • filterIndexed:与filter不同的是可以引入元素索引作为入参
    • filterNotNull:过滤null
    • filterIsInstance:过滤掉不是指定类型的元素
    • partition:按照lambda表达式条件,将集合分成两部分
    • any 、none、all:判断集合中是否有满足条件的元素、是否都不满足条件、是否都满足条件

    转换相关的

    • map:按照lambda表达式条件,作用于集合中的每个元素,并返回结果列表
    • mapIndexed:与map不同的是可以引入元素索引作为入参
    • mapNotNull:在map的基础上,结果列表中产生了null
    • mapIndexedNotNull:与mapNotNull不同的是可以引入元素索引作为入参
    • mapKeys:用于转换Map类型集合中的key
    • mapValues:用于转换Map类型集合中的value
    • zip:用于在两个集合中,构建相同位置的元素对,两个集合元素长度不一致以短的集合为基准
    • unzip:zip的反操作,将集合对展开为两个集合
    • flatten:将多个集合扁平化成一个集合,不关心每个集合的长度是否一致
    • flatMap:可以理解为map操作之后,再调用flatten,它的返回值是一个结果列表

    特定集合类型相关的

    • getOrNull
    • getOrElse
    • subList
    • indexOf
    • lastIndexOf
    • indexOfFirst
    • indexOfLast
    • filterKeys
    • filterValues

    1.2 作用域函数

    Kotlin内置的一系列可以对数据做变换的函数,与集合的操作符非常相似,但集合操作符只能用于集合,而作用域函数可以用于对所有对象做一系列操作。

    • let {...}
    • run {...}
    • also {...}
    • apply {...}
    - 有闭包参数 无闭包参数
    返回闭包结果 let run
    不返回闭包结果 also apply

    takeIf的闭包返回一个判断结果,为false时,takeIf函数会返回空;takeUnless 与 takeIf 刚好相反, 闭包的判断结果,为true时函数会返回空。

    • takeIf {...}
    • takeUnless {...}

    with比较特殊,不是以扩展方法的形式存在的,而是一个顶级函数。

    • with(T) {...}
    fun main() {
        val user = UserInfo(9802830, "Rose", "http://oioe.i/23.png", 1, 1, "17789876555")
    
        val letResult = user.let { "let::${it.nickname}" }
        println(letResult)
        val runResult = user.run { "run::${this.mobile}" }
        println(runResult)
        println("---")
    
        user.also {
            println("also::${it.uid}")
        }.apply {
            println("apply::${this.gender}")
        }.nickname = "hello"
        println(user.nickname)
        println("---")
    
        user.nickname = "Lily"
        user.takeIf { it.nickname?.length!! > 0 }?.also { println("姓名为${it.nickname}") } ?: println("姓名为空")
        user.takeUnless { it.nickname?.length!! > 0 }?.also { println("姓名为空") } ?: println("姓名为${user.nickname}")
        println("---")
    
        with(user) {
            this.head = "http://oioe.i/25.png"
            this.mobile = "17789876558"
        }
        println(user)
    
    }
    

    1.3 操作符的实现原理

    本质上都是扩展函数或者是扩展函数的形式为代码做一系列的扩展操作。

    //map操作示例
    public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> {
        return mapTo(ArrayList<R>(collectionSizeOrDefault(10)), transform)
    }
    
    public inline fun <T, R, C : MutableCollection<in R>> Iterable<T>.mapTo(destination: C, transform: (T) -> R): C {
        for (item in this)
            destination.add(transform(item))
        return destination
    }
    
    //flatMap操作示例
    public inline fun <T, R> Iterable<T>.flatMap(transform: (T) -> Iterable<R>): List<R> {
        return flatMapTo(ArrayList<R>(), transform)
    }
    
    public inline fun <T, R, C : MutableCollection<in R>> Iterable<T>.flatMapTo(destination: C, transform: (T) -> Iterable<R>): C {
        for (element in this) {
            val list = transform(element)
            destination.addAll(list)
        }
        return destination
    }
    

    2. 高级特性

    2.1 解构声明

    就是将一个对象拆解成若干个变量,其本质将对象解析成一组属性,然后通过调用与之对应的component()方法得到属性的值,再赋值给各自的局部变量。

    fun main() {
        val map = mapOf("深圳" to "中国", "孟加拉" to "印度")
        for ((city, country) in map) {
            println("$city belongs $country")
        }
    
        val (uid2, nickname, _, _, _, mobile) = UserInfo(9802830, "Rose", "http://oioe.i/23.png", 1, 1, "17789876555")
        println("My nickname is $nickname,uid is $uid2 , my mobile phone is $mobile")
    }
    

    2.2 中缀表达式

    所谓中缀表达式就是不需要点和括号的方法调用,其本质还是函数调用。

    enum class CompareResult {
        MORE, LESS, EQUAL
    }
    
    infix fun Int.vs(num: Int): CompareResult =
        when {
            this - num > 0 -> CompareResult.MORE
            this - num < 0 -> CompareResult.LESS
            else -> CompareResult.EQUAL
        }
    
    fun main() {
        //自定义示例
        println(1 vs 6)
        println(9 vs 6)
        println(6 vs 6)
    
        //库自带中缀示例 to、step
        mapOf("深圳" to "中国", "孟加拉" to "印度")
    
        for (i in 1..10 step 2) {
            println(i)
        }
    }
    
    • 中缀表达式实际上就是函数调用,如果把infix关键字去掉,那么跟纯粹函数调用方式没有任何区别。比如5.vs(6)、1.to("A")、element.into(list)等。只有加了infix关键字后,才可以使用中缀的调用方式:例如 1 to "A", 5 vs 6。
    • 中缀表达式让我们的代码更加接近自然语言,但不是所有的函数都能写成中缀调用,首先必须满足一个条件就是函数的参数只有一个。然后再看这个函数的参与者是不是只有两个元素,这两个元素可以是两个数,可以是两个对象,可以是集合等。

    2.3 内联函数的特殊性

    Lambda 表达式最大的特点是可以作为参数传递。当定义一个闭包作为参数的函数,称这个函数为高阶函数。在使用高阶函数时,为了大幅提升高阶函数的性能,使用了内联函数,在编译阶段,编译器将会把内联函数拆分,直接插入到调用出。(ps:如果一个 inline 函数是很大的,那他会大幅增加调用它的那个函数的体积。)

    • 在Kotlin中,内部Lambda是不允许中断外部函数执行的。
    • inline 的 Lambda 可以中断外部函数调用。(思考下为什么可以中断外部调用?)
      • 因为内联是将代码平铺,平铺以后就不存在Lambda内外代码块之分了
    • crossinline 不允许 inline 的Lambda中断外部函数执行。
    • noinline 拒绝内联。(思考下test2函数使用了inline后为什么还需要使用noinline来修饰第二个lambda表达式?)
      • 因为需要返回的lambda高阶函数是不能被平铺开的,当它被内联以后,平铺开后会修改外部函数的返回值类型
    
    val runnable = Runnable {
        println("runnable task...")
    }
    
    val successCallback: () -> Int = {
        println("do something task...")
        100
    }
    
    fun main() {
        //内部Lambda是不允许中断外部函数执行的
        test4(runnable::run, runnable::run)
        println("--- ---")
    
        //inline函数的return直接中断了外部函数的调用
        val userInfo = UserInfo(9802830, "Rose", "http://oioe.i/23.png", 1, 1, "17789876555")
        userInfo.let {
            it.mobile?.apply {
                println("$this do something background task...")
                //return
            }
        }
        println("--- ---")
    
        test1 {
            println("hello 1-1")
            return@test1 //可以根据实际的情况来决定是不是要执行后面的代码
            println("hello 1-1-1")
        }
        println("--- ---")
    
        test2(successCallback) {
            return
            println("hello 2-1")
        }
        println("--- ---")
    
        test3({
            println("hello 3-1")
        }, runnable::run)
    }
    
    inline fun test1(crossinline block: () -> Unit) {
        block()
        return
    }
    
    inline fun test2(noinline block1: () -> Int, block2: () -> Unit): () -> Int {
        block1.invoke()
        block2()
        println("test 2")
        return block1
    }
    
    inline fun test3(block: () -> Unit, block2: () -> Unit) {
        block.invoke()
        block2.invoke()
        println("test 3")
    }
    
    fun test4(block: () -> Unit, block2: () -> Unit) {
        block.invoke()
        block2.invoke()
        println("test 4")
    }
    
    

    2.4 操作符重载

    运算符重载需要使用关键字operator修饰,其余定义与函数相同。 运算符的数量毕竟是有限的,有时并不一定有合适的,此时可以考虑前面的中缀表达式,要是觉得麻烦,可以直接考虑扩展函数。

    • 一元前缀操作:+ - !
    • 递增与递减:++ --
    • 二元操作:+ - * / % ..
    • In操作:contains
    • 索引访问操作:get/set
    • 调用操作:invoke
    • 广义赋值:+= -+ *= /= %=
    • 相等与不等操作符:== !=
    • 比较操作符:> < >= <=
    data class Point(val x: Int, val y: Int)
    
    operator fun Point.unaryMinus() = Point(-x, -y)
    operator fun Point.unaryPlus() = Point(+x, +y)
    
    val point = Point(10, 20)
    
    fun main() {
        println(-point)  // prints "Point(x=-10, y=-20)"
        println(+point)  // prints "Point(x=-10, y=-20)"
    
        val listOf = listOf("深圳", "孟加拉", "拉斯维加斯")
        val myCity = "拉斯维加斯"
        println(myCity in listOf)
    
        println(listOf.get(0))
        println(listOf[1])
    }
    

    3. 必须掌握的命令

    javap [option] xxx.class

    这个命令可以反编译一个class文件,可以方便的让我们知道Kt代码在编译以后处于怎样的状态,以及帮助我们分析自己的代码。

    • -help --help -? 输出此用法消息
    • -version 版本信息
    • -v -verbose 输出附加信息
    • -l 输出行号和本地变量表
    • -public 仅显示公共类和成员
    • -protected 显示受保护的/公共类和成员
    • -package 显示程序包/受保护的/公共类和成员 (默认)
    • -p -private 显示所有类和成员
    • -c 对代码进行反汇编
    • -s 输出内部类型签名
    • -sysinfo 显示正在处理的类的系统信息 (路径, 大小, 日期, MD5 散列)
    • -constants 显示最终常量
    • -classpath <path> 指定查找用户类文件的位置
    • -cp <path> 指定查找用户类文件的位置
    • -bootclasspath <path> 覆盖引导类文件的位置

    相关文章

      网友评论

          本文标题:Kotlin高级入门

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