JYM咱们好,好久没来写文了。

今日带给咱们 Kotlin 的内容,或许一些常重视我的朋友也发现了,在我之前的文章中就开始用 Kotlin 代码做代码示例了,这是由于最近一年我都在高强度运用 Kotlin 进行后端开发。

相信许多安卓开发的朋友早就开始用上 Kotlin 了,可是许多后端对这门言语应该仍是不太了解,反正在我的朋友圈里没有见到过用 Kotlin 写后端的程序员存在,在我亲身用了一年 Kotlin 之后现已不太想用 Java 进行代码开发了,起码在开发功率方面就现已是天差地别了,所以今日特别给咱们分享一下 Kotlin 的好,期望能带领更多人入坑。

1. 第一段代码

许多人说 Kotlin 便是披了一层语法糖的 Java,由于它百分百兼容 Java,乃至能够做到 Kotlin 调用 Java 代码。

其实我对这个说法是赞同的,可是又不完全相同,由于 Kotlin 有自己的语法、更有自己的编译器、还有着多端支撑,更有着自己的规划方针。

我更倾向于把 Kotlin 看成一个 JVM 系言语,就像 Scala 言语相同,仅仅刚好 Kotlin 有一个规划方针便是百分百兼容 Java。

在言语层面,Kotlin 简直是借鉴了市面上一切的现代化言语的强壮特性,协程、函数式、扩展函数、空安全,这些广受好评的特性悉数都有。

并且从我个人感受来看,我用过 Java、Kotlin、JS、Go、Dart、TS、还有一点点 Python,我觉得 JS 和 Kotlin 的语法是比较便利易用的,Dart、Java 和 Go 的语法都不是太便利易用,语法的简略也在一定程度上减少了开发者的心智负担。

下面我将用一段代码,来简略阐明一下 Kotlin 常见的语法特性:

fun main(args: Array<String>) {
    val name = "Rookie"
    // Hello World! Rookie
    println("Hello World! $name")
    // Hello World! 82,111,111,107,105,101
    println("Hello World! ${name.chars().toList().joinToString(",")}")
    test(createPerson = { Person("Rookie", 25) })
}
data class Person(
    var name: String = "",
    var age: Int = 0,
)
fun test(createPerson: () -> Person, test : String = "test"): Person {
    val person = createPerson()
    // Person(name=Rookie, age=25)
    println(person)
    return person
}

上面是一段简简略单的 Kotlin 代码,可是却能够看出 Kotlin 的许多特性,请听我娓娓道来~

  1. Kotlin 的发动办法也是 main 函数,可是 Kotlin 移除了一切根底类型,一切皆目标,比方 Java 中的数组对应的便是 Array 类,int 对应的是 Int 类。
  2. Kotlin 运用类型揣度来声明类型,一共有两个关键字,val 代表这是一个不可变变量,var 代表这是一个可变的变量,这两个关键字选用我感觉比 JS 还要好。
  3. Kotlin 代码每一行不需要英文分号结尾。
  4. Kotlin 支撑字符串模板,能够直接字符串中运用 ‘$’ 符号放置变量,假如你想放置一个函数的计算结果,需要用 ‘${}’ 来包裹。
  5. Kotlin 是一个函数式言语,支撑高阶函数,闭包、尾递归优化等函数式特性。
  6. Kotlin 为了简化 Java Bean,支撑了数据类 data class,它会自动生成无参结构、getter、setter、equals()、hashCode()、copy()、toJSON()、toString() 办法。
  7. Kotlin 的函数关键字是 fun,回来值在函数的最终面,变量名在类型的前面,简直新式言语都是这样规划的,能够显着感受到言语规划者想让咱们更多重视事务意义而非数据类型。
  8. Kotlin 具有一些相似 go 和 Python 的内置函数,比方 println。
  9. Kotlin 的函数参数支撑默认值。
  10. Kotlin 不支撑多参数回来,可是为了处理这个问题它内置了两个类:Pair 和 Triple,别离能够包装两个回来值和三个回来值。

2. 根底常用特性

了解了一些 Kotlin 的根底语法之后,我再来介绍一些常用的根底特性。

第一个便是空安全和可空性。

Kotlin 中的变量能够声明为非空和可空,默认的声明都对错空,假如需要一个变量可空的,需要在类型后面加一个问号,就像这样:

fun start() {
    val name1 : String = ""
    val name : String? = null
}

函数的参数声明也相同,也会区分非空和可空,Kotlin 编译器会对代码上下文进行查看,在函数调用途也会对变量是否可空进行一致性查看,假如不经过则会有编译器提醒,我是强烈建议不必可空变量,一般都能够经过默认值来处理。

那么假如你接手的是前人代码,他声明变量为可空,可是期望为空的时分传递一个默认值,则能够运用这个语法进行处理:

fun start() {
    val name : String? = null
    println(name ?: "Rookie")
}

这是一个相似三元表达式的语法(Elvis 运算符),在 Kotlin 中极端常见,除此之外你还能够进行非空调用:

fun start() {
    val name : String? = null
    println(name?.chars() ?: "Rookie")
}

这段代码就标明:假如变量不为空就调用 chars 办法,假如为空则回来默认值 Rookie,在一切可空变量上都支撑这种写法,并且支撑链式调用。

第二个常用特性是反常处理, 写到这儿忽然想到了一个标题,Kotlin 的反常处理,那叫一个高雅!!!

fun start() {
    val person = runCatching {
        test1()
    }.onFailure {
    }.onSuccess {
    }.getOrNull() ?: Person("Rookie", 25)
}
fun test1() : Person {
    return Person()
}

这一段代码中的 test1 办法你能够当作一个长途调用办法或许逻辑办法,对了,这儿隐含了一个语法,便是一个函数中的最终一行的计算结果是它的回来值,你不需要显现的去写 return。

我运用 runCatching 包裹咱们的逻辑办法,然后有三个链式调用:

  1. onFailure:当逻辑办法报错时会进入这个办法。
  2. onSuccess:当逻辑办法履行成功时会进入这个办法。
  3. getOrNull:当逻辑办法履行成功时正常回来,履行失利时回来一个空变量,然后咱们紧跟一个 ?: ,这代表当回来值为空时咱们回来自界说的默认值。

如此一来,一个反常处理的闭环就完结了,每一个环节都会被考虑到,这些链式调用的办法都是可选的,假如你不手动调用途理会有默认的处理办法,咱们伙觉得高雅吗?

第三个特性是改善后的流程控制。

fun start() {
    val num = (1..100).random()
    val name = if (num == 1) "1" else { "Rookie" }
    val age = when (num) {
        1 -> 10
        2 -> 20
        else -> { (21..30).random() }
    }
}

咱们先声明一个随机数,然后根据条件判别句子回来不同的值,其间 Java 中的 Switch 由 When 语法来替代。

并且这儿每一段表达式都能够是一个函数,咱们能够回想一下,假如你运用 Java 来完结经过条件回来不同变量的逻辑会有多麻烦。

假如咱们在不了解 Kotlin 的情况下尝试用更简略的办法来写逻辑,能够问问相似 ChatGPT 这种对话机器人来进行辅佐你。

3. 常用内置函数

就像每个变量类型都有 toString 办法相同,Kotlin 中的每个变量都具有一些内置的扩展函数,这些函数能够极大的便利咱们开发。

apply和also

fun start() {
    val person = Person("Rookie", 25)
    val person1 = person.apply {
        println("name : $name, age : $age, This : $this")
    }
    val person2 = person.also {
        println("name : ${it.name}, age : ${it.age}, This : $it")
    }
}

这两个函数调用之后都是履行函数体后回来调用变量自身,不同的是 apply 的引证为 this,内部取 this 变量时不需要 this.name 能够直接拿 name 和 age 变量。

而 also 函数则默认有一个 it,it 便是这个变量自身的引证,咱们能够经过这个 it 来获取相关的变量和办法。

run 和 let

fun start() {
    val person = Person("Rookie", 25)
    val person1 = person.run {
        println("name : $name, age : $age, This : $this")
        "person1"
    }
    val person2 = person.let {
        println("name : ${it.name}, age : ${it.age}, This : $it")
        "person2"
    }
}

run 函数和 let 函数都支撑回来与调用变量不同的回来值,只需要将回来值写到函数最终一行或许运用 return 句子进行回来即可,上例中 person 变量进行调用之后的回来结果便是一个 String 类型。

在运用上的详细差异也便是引证目标的指向不同,详细更多差异能够看一下网络上总结,我这儿标明用法就能够了。

除了这四个函数之外,还有许多的相似函数帮咱们来做一些很高雅的代码处理和链式调用,可是我底子没有用过其它的函数,这四个函数对我来说现已足够了,有兴趣的朋友能够慢慢开掘。

4. 扩展函数与扩展特点

上文了咱们举了几个常见的内置函数,其实他们都是运用 Kotlin 的扩展函数特性完成的。

所谓扩展函数便是能够为某个类添加扩展办法,比方给 JDK 中的 String 类添加一个 isRookie 办法来判别某个字符串是否是 Rookie:

fun start() {
    val name = "rookie"
    println(name.isRookie())
}
fun String.isRookie(): Boolean {
    return this == "Rookie"
}

this 代表了当时调用者的引证,利用扩展函数你能够很便利的封装一些常用办法,比方 Long 型转时刻类型,时刻类型转 Long 型,不比像以前相同再用东西类做调用了。

除了扩展函数,Kotlin 还支撑扩展特点:

fun start() {
    val list = listOf(1, 2, 3)
    println(list.maxIndex)
}
val <T> List<T>.maxIndex: Int
    get() = if (this.isEmpty()) -1 else this.size - 1

经过界说一个扩展特点和界说它的 get 逻辑,咱们就能够为 List 带来一个全新特点——maxIndex,这个特点用来回来当时 List 的最大元素下标。

扩展函数和扩展特点多用于关闭类,比方 JDK、第三方 jar 包作为扩展运用,它的实际运用效果其实和东西类是相同的,只不过愈加高雅。

不过借用这个能力,Kotlin 为一切的常用类都添加了一堆扩展,比方 String:

Kotlin的语法糖到底有多甜?

基本上你能够想到的大部分函数都现已被 Kotlin 内置了,这便是 Kotlin 的语法糖。

5. Kotlin的容器

终于来到咱们这篇文章的大头了,Kotlin 中的容器基本上都是:List、Map 扩展而来,作为一个函数式言语,Kotlin 将容器分为了可变与不可变。

咱们先来看一下遍及的用法:

fun start() {
    val list = listOf(1, 2, 3)
    val set = setOf(1, 2, 3)
    val map = mapOf(1 to "one", 2 to "two", 3 to "three")
}

上面的例子中,咱们运用三个内置函数来便利的创立对应的容器,可是此刻创立的容器是不可变的,也便是说容器内的元素只能读取,不能添加、删去和修改。

当然,Kotlin 也为此类容器添加了一些办法,使其能够便利的添加元素,但实际行为并不是真的往容器内添加元素,而是创立一个新的容器将本来的数据复制过去:

fun start() {
    val list = listOf(1, 2, 3).plus(4)
    val set = setOf(1, 2, 3).plus(4)
    val map = mapOf(1 to "one", 2 to "two", 3 to "three").plus(4 to "four")
}

假如咱们想要创立一个能够添加、删去元素的容器,也便是可变容器,能够用以下函数:

fun start() {
    val list = mutableListOf(1, 2, 3)
    val set = mutableSetOf(1, 2, 3)
    val map = mutableMapOf(1 to "one", 2 to "two", 3 to "three")
}

讲完了,容器的创立,能够来聊聊相关的一些操作了,在 Java 中有一个 Stream 流,在 Stream 中能够很便利的做一些常见的函数操作,Kotlin 不仅完全承继了过来,还加入了许多办法,大约能够包含以下几类:

  1. 排序:sort
  2. 乱序:shuffle
  3. 分组:group、associate、partition、chunked
  4. 查找:filter、find
  5. 映射:map、flatMap
  6. 规约:reduce、min、max

由于函数实在太多,我不能一一列举,只能给咱们举一个小例子:filter:

Kotlin的语法糖到底有多甜?

一个 filter 有这么多种多样的函数,简直能够包容你一切的场景,这儿说两个让我感觉到惊喜的函数:chunked 和 partition。

chunked 函数是一个分组函数,我常用的场景是防止恳求量过大,比方在批量提交时,我能够将一个 list 中的元素进行 1000 个一组,每次提交一组:

fun start() {
    val list = mutableListOf(1, 2, 3)
    val chunk : List<List<Int>> = list.chunked(2)
}

示例代码中为了让咱们看的清楚我成心声明了类型,实际开发中能够不声明,会进行自动揣度。

在上面这个例子中,我将一个 list 进行每组两个进行分组,最终得到一个 List<List> 类型的变量,接下来我能够运用 forEach 进行批量提交,它底层经过 windowed 函数进行调用,这个函数也能够直接调用,有兴趣的朋友能够研讨一下效果,经过名字大约能够知道是相似滑动窗口。

partition 你能够将其看作一个分组函数,它算是 filter 的补充:

fun start() {
    val list = mutableListOf(1, 2, 3)
    val partition = list.partition { it > 2 }
    println(partition.first)
    println(partition.second)
}

它经过传入一个布尔表达式,将一个 List 分为两组,回来值是上文提到过的 Pair 类型,Pair 有两个变量:first 和 second。

partition 函数会将契合条件的元素放到 first 中去,不契合条件的元素放到 second 中,我自己的运用的时分许多是为了日志记录,要把不处理的元素也记录下来。

容器与容器之间还能够直接经过相似:toList、toSet之类的办法进行转化,非常便利,转化 Map 我一般运用 associate 办法,它也有一系列办法,首要效果便是能够转化过程中自己指定 Map 中的 K 和 V。

6. 完毕语

不知不觉都现已快四千字了,我现已要完毕这篇文章了,可是依然发现简直什么都没写,也对,这仅仅一篇给咱们普及 Kotlin 所带来功率的提升的文章,而不是专精的技术文章。

正如我在标题中写的那样:Kotlin 的语法糖到底有多甜?Kotlin 的这一切我都将其当作语法糖,它能极大提高我的开发功率,可是一些真正 Kotlin 能够做到而 Java 没有做到的功用我却没有运用,比方:协程。

由于我一直是运用 Kotlin 写后端,而协程的运用场景我从来没有遇到过,或许做安卓的朋友更简单遇到,所以我没有对它进行举例,对于我来说,Kotlin 能为我的开发大大提效就现已很不错了。

运用 Kotlin 有一种运用 JS 的感觉,有时分能够一个办法最初就写一个 return,然后链式调用一直到办法完毕。

我仍是蛮期望 Java 开发者们能够转到 Kotlin,感受一下 Kotlin 的魅力,毕竟是百分百兼容。

在这儿要说一下我运用的版别,我运用的是 JDK17、Kotlin 1.8、Kotlin 编译版别为 1.8,也便是说 Kotlin 生成的代码能够跑在最低 JDK1.8 版别上面,这也是一个 Kotlin 的好处,你能够经过晋级 Kotlin 的版别体验最新的 Kotlin 特性,可是呢,你的 JDK 渠道不必变。

对了,Kotlin 将反射封装的极好,喜欢研讨的朋友也能够研讨一下。

好了,这篇文章就到这儿,期望咱们能帮我积极点赞,提高更新动力,人生苦短,我用KT。