<p style="text-align:center;color:#42A5F5;font-size:2em;font-weight: bold;">前言
即使每天 10 点下班, 其实需求很多, 我也要用这腐朽的声带喊出: 我要学习, 我要写文章!!
又是一篇 Kotlin 的文章, 为啥... 还不是因为工作需要. 毫无疑问, 最好的学习方式是通过官方文档去学习. 不过个人觉得官方文档多多少少有一些不够高效.
中文官方文档 https://www.kotlincn.net/docs/reference/
因此这篇是从我学习的个人视角以文档的形式去输出 Kotlin 语言基础的学习.
不扯淡了, 开整.
<p style="text-align:center;color:#42A5F5;font-size:2em;font-weight: bold;">正文
1, 高阶函数
高阶函数是将函数用作参数或返回值的函数.
个人为什么把这个语言特性放在第一位, 因为我觉得这是我们 Java 语言中所不支持的. 然而这个特性基本贯穿整个 Kotlin 语言体系. 所以个人把它放到了第一位, 也希望各位小伙伴能够重视这个小小的特性. 先看看一个小小的 demo, 感受一下这个特别特性:
- // 函数定义
- fun funtion(num: Int, innerFun: (Int) -> Int): Int {
- return innerFun(num)
- }
简单解释一下上边的代码. 我们先看函数定义, 这里定义了一个名为 funtion 并且返回值为 Int 的函数, 此外这个函数接受一个 Int 参数和一个函数类型的参数(这个函数需要传递一个 Int 参数, 并且返回一个 Int 值).
接下来我们调用一下:
- // 函数调用
- val result = funtion(1) {
- it + 666
- }
对于 Lambda 来说, 入参如果是一个的话可以用 it 来表示
说实话, 第一次我看到这种调用的时候, 是一脸懵逼的. 不知道刚入坑的小伙伴是不是和我一样的感受? 因为这种写法包含小 tips:
我们的 innerFun 是可以被简化为一个 Lambda, 而当 Lambda 作为函数的最后一个参数时, 是可以将其写在函数之外的. 也就是 Demo 中的 funtion(1){}.
此外我们要注意一点, 这里我们的函数实例, 并没有 return, 这是因为. lambda 表达式中的最后一个表达式是返回值
实际上这就是相当于调用了 funtion 方法的俩个参数, 咱们换一种传统的写法, 二者是等价的:
- val inner = fun(num: Int): Int {
- return num + 1
- }
- val result = funtion(1, inner)
OK, 接下来咱们趁热打铁, 再感受感受函数操作的 "骚操作". 接下来我们看一看其实情况下的用法:
- // 函数定义 1
- fun funtion(num: Int, innerFun: (Int, Int) -> Int): Int {
- return innerFun(num, num + 1)
- }
- // 函数调用 1(上文提到, 入参为 1 个可以用 it 表示, 那么俩个或者多个呢? 我们可以如下, 自定义)
- val result = funtion(1) {
- num1, num2 ->
- num1 + num2 + 666
- }
- // 函数调用 2(除了 Lambda 的调用方式, 我们还可以用匿名函数, 二者是一样的)
- val result = funtion(1, fun(num1: Int, num2: Int): Int {
- return num1 + num2 + 666
- }
OK, 关于高阶函数的内容, 就聊这么多, 因为有了这个其实, 很多内容也就很好上手了. 接下来就让我们一同看看基于高阶函数的内置封装.
2, 操作符
集合操作符
个人觉得, Kotlin 操作符中. 开发中颇为常用的是集合操作符, 比如我们有时需要对一些数据进行连续的转化, 我们可能会使用 RxJava;
- Observable.create(new ObservableOnSubscribe<List<CustomModel>>() {
- @Override
- public void subscribe(ObservableEmitter<List<CustomModel>> e) throws Exception {
- // 省略构建 List<CustomModel>
- e.onNext(data);
- }
- }).flatMap(new Function<List<CustomModel>, ObservableSource<CustomModel>>() {
- @Override
- public ObservableSource<CustomModel> apply(List<CustomModel> customModels) throws Exception {
- return Observable.fromIterable(customModels);
- }
- }).map(new Function<CustomModel, String>() {
- @Override
- public String apply(CustomModel customModel) throws Exception {
- return customModel.name;
- }
- }).subscribe(new Consumer<String>() {
- @Override
- public void accept(String s) throws Exception {
- // 操作 s
- }
- });
这里简单, 写了一个 RxJava 的 Demo: 我们有一个 List 集合, 先把它一个个发送, 然后把每一个数据转成 String, 最后去 String 进行处理.
而在 Kotlin 中, 应对上述的需求, 我们该怎么做? 由于操作符的支持, 我们可以极为方便的进行这类操作:
- val data = arrayListOf<CustomModel>(// ... 省略构建的过程)
- val newData=data.map {
- it.name
- }.forEach {
- // 操作 it
- }
用法很简单, 乍一看很唬人. 但是其实很简单, 回忆一下我们再开篇奠定的高阶函数的基础. 这里其实就是调用了 data 的扩展函数 map(拓展函数是一种语法, 可以拓展现有的类. 比如这个 map 就是拓展了 Iterable, 下文我们会展开它的实现). 对它进行类型转变, 然后进而调用 forEach 自动对这个集合进行遍历.
接下来让我们点进去, 看一看具体的实现:
- // 注意看, 这里 map 的 ** 返回值 **, 是一个 List<R>, 也就是说最终将返回 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
- }
说白了, 这里的集合操作符就是把转化的实现通过参数的形式暴露出来, 由我们自行去处理, 其他的事情, 有内部封装去做.
当然, 这其中还有很多有趣的操作符, 各位小伙伴可以自行去了解. 不过原理都是大同小异~
3, 作用域函数
上述我们聊的是作用在集合上的操作符, 说白了. 它们就是拓展在对应集合类上的函数而已. 那么可能有小伙伴会问, 有没有作用在对象上的? 没错, 是有的. 也就是我们接下来要聊的: 作用域函数.
其实很常见:
- "Kotlin".let{
- it.toInt()
- }
- "Kotlin".run{
- this.toInt()
- }
- "Kotlin".apply{
- }
- "Kotlin".also{
- }
- public inline fun <T, R> T.let(block: (T) -> R): R
- public inline fun <T, R> T.run(block: T.() -> R): R
- public inline fun <T> T.apply(block: T.() -> kotlin.Unit): T
- public inline fun <T> T.also(block: (T) -> kotlin.Unit): T
咱们看一下, 它们哥四个的定义, 其实很清晰. 这里以返回值的类型, 俩俩分类:
let/run. 这俩个函数都可以返回另一种类型; 不同在于 let 有入参, 而 run 没有.
also/apply. 这俩个函数返回值都是本身, 其中 apply 没有入参, 而 also 有.
不过即使没有入参, 也可以通过 this, 得到本身.
上述函数的作用还是挺方便的, 比如:
- Model model ;
- // ... 省略初始化过程
- if(model!=null){
- // 省略大段的操作逻辑
- }
这种写法, 在 kotlin 中可以这么写:
- model?.alse{
- // 省略大段的操作逻辑
- }
- // 有需求我们可以继续调用它的作用域函数
如果你还有一些 else 操作, 那么没辙了, kotlin 中也只能老老实实 if-else
当然, 还有一些有趣的作用域函数, 比如在 Java 中:
- boolean isTure;
- // 省略 boolean 判断逻辑
- if(isTure){
- // 省略大段的操作逻辑
- }
到 Kotlin 中可以这样来搞:
某对象. takeIf{
- // boolean 判断逻辑
- }?.{
- // 省略大段的操作逻辑
- }
- // 有需求我们可以继续调用它的作用域函数
takeIf: 会根据入参的函数的返回值 (true/false), 决定自己(takeIf) 的返回值是 null 还是调用者. 如果是 false, 那么就会返回 null, 因此这里使用? 的方式继续去调用后续操作.
我个人比较喜欢作用域函数, 因为可以省略很多 if. 接下来我们来换个稍稍复杂的逻辑: 已知一个方法, 入参是一个 Model, 我们要判断当这个 Model 对象的 age 属性大于 10, 打印这个 Model 对象的名字.
对于 Java 来说, 我们可能这么写:
- public void fun(Model model) {
- // 写了 3 个 if.
- if (model != null) {
- if (model.age>10){
- if (model.name!=null){
- // 打印 model.name
- }
- }
- }
- }
- class Model {
- String name;
- int age;
- }
对于 Kotlin 来说, 我们可以这么写:
- fun funtion(model : Model){
- model?.takeIf{
- it.age> 10
- }?.let{
- // 打印 it.name
- }
- }
是不是简洁了很多? 但是单说简洁这种东西, 似乎并没有什么卵用... 不过写起来, 真的很爽! 不信, 大家可以试试~~
<p style="text-align:center;color:#42A5F5;font-size:2em;font-weight: bold;">尾声
这篇文章, 想聊的内容就这么多. 个人认为其实理解了高阶函数,(PS: 不知道为啥起了个名叫高阶函数, 整得好像很高级似的.)Kotlin 就可以很快的上手, 并且能感受到 Kotlin 写起来的爽快之处~
这里的确有些安利的意味. 那是因为自己最开始对新技术其实接受度并不高, 总认为新东西有各种各样的问题... 当自己去了头条之后, 才发现身边的同事那种高昂的学习劲头,(Kotlin? 对他们来说不叫新东西...Jatpack,Flutter 都已经在线上项目中跑了)自己还有什么理由说: 求求别更新了, 我学不动了...
还是那句话: 干就完了!
Kotlin 快速入坑指南(干货型文档)
来源: http://www.bubuko.com/infodetail-2877716.html