Kotlin高级入门

示例项目地址: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> 覆盖引导类文件的位置
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352

推荐阅读更多精彩内容