全网最全面的由浅到深的Kotlin基础教程(七)

前言

本篇文章接着上一篇文章全网最全面的由浅到深的Kotlin基础教程(六)继续进阶学习kotlin,建议看完上一篇文章,再来看本篇文章。本篇主要分析一些常用的kotlin函数源码,以及用kotlin简单实现Rxjava的几个操作符。坚持将kotlin系列的前6篇看完的,那么恭喜你再将这最后一篇看完,你就成为一名合格的kotlin开发者了。

1. kotlin标准函数源码解析

1.1 apply

源码及分析注释如下:

/*
public 访问修饰符
inline fun 内联函数
<T> 泛型定义
T.apply 泛型T的扩展函数
(block: T.() -> Unit) 输入参数为一个名为block的lambda表达式,
		其中T.() 是一个泛型扩展函数,这种写法使得lambda表达式持有T的this对象,
		当做固定写法来理解即可。
: T apply函数的返回类型仍然为T
*/
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
    //契约函数,暂时不用关注,可以不写的
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    //执行lambda表达式的逻辑
    block()
    //最终仍然返回T对象本身
    return this
}

1.2 also

与apply函数的意义一样,只是also函数的lambda表达式持有it,而apply函数的的lambda表达式持有this,源码及分析注释如下:

/*
public 访问修饰符
inline fun 内联函数
<T> 泛型定义
T.also 为泛型T定义扩展函数also
block: (T) -> Unit also函数中传入的参数是lambda表达式,该lambda表达式有一个T泛型的参数,返回值为空
: T also函数的最终返回值仍然为T泛型
*/
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    //执行lambda表达式,将T泛型的this对象传入block函数,因此block通过it持有T泛型的this对象
    block(this)
    //不管你上面的lambda表达式干了啥,最终仍然返回T泛型对象本身
    return this
}

1.3 run

源码及分析注释如下:

/*
public 访问修饰符
inline fun 内联函数
<T, R> 定义两个泛型
T.run 为T泛型定义扩展函数run
block: T.() -> R run函数有一个参数是lambda表达式,该lambda表达式通过T泛型扩展函数,拥有T泛型的this对象,返回类型为R
: R run函数的返回类型是R泛型
*/
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    //执行lambda表达式
    //因为lambda表达式拥有T泛型的this对象
    //因为lambda表达式返回的是R,因此返回值由lambda表达式的最后一行决定
    //return this.block()  //this可以省略
    return block()
}

1.4 let

与run函数的意义一样,只是let函数的lambda表达式持有it,而run函数的的lambda表达式持有this,源码及分析注释如下:

/*
public 访问修饰符
inline fun 内联函数
<T, R> 定义两个泛型
T.let 为T泛型定义扩展函数let
block: (T) -> R lambda表达式参数,输入T泛型(lambda表达式如果只有一个参数,则参数名默认为it),输出为R泛型
: R 最终返回的类型是R泛型
*/
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    //执行lambda表达式
    //因为lambda表达式传入的是this,因此lambda表达式通过it持有T泛型的对象
    //因为lambda表达式输入的T,返回的是R,因此返回值由lambda表达式的最后一行决定
    return block(this)
}

1.5 with

与run函数的意义一样,只是调用方式不一样而已,run是泛型扩展函数,而with是普通的函数,源码及分析注释如下:

/*
public 访问修饰符
inline fun 内联函数
<T, R> 定义两个泛型
with(receiver: T, block: T.() -> R) 这是一个普通的函数,并非扩展函数,包括两个参数,第一个T泛型的参数,第二个是lambda表达式,lambda表达式通过T泛型扩展函数,拥有T泛型的this对象,返回类型为R
: R 最终返回类型是R
*/
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    //block的最后一行是返回值
    return receiver.block()
}

1.6 takeIf

源码及分析注释如下:

/*
public 访问修饰符
inline fun 内联函数
<T> 泛型定义
T.takeIf 为泛型T定义扩展函数takeIf
predicate: (T) -> Boolean takeIf函数中传入的参数是lambda表达式,该lambda表达式有一个T泛型的参数,返回值为Boolean
: T? takeIf函数最终返回的可空的T泛型类型
*/
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? {
    contract {
        callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
    }
    //如果lambda表达式返回true,则takeIf返回T泛型this对象本身,否则返回null
    return if (predicate(this)) this else null
}

1.7 takeUnless

与takeIf的意义相反,源码及分析注释如下:

/*
public 访问修饰符
inline fun 内联函数
<T> 泛型定义
T.takeUnless 为泛型T定义扩展函数takeUnless
predicate: (T) -> Boolean takeUnless函数中传入的参数是lambda表达式,该lambda表达式有一个T泛型的参数,返回值为Boolean
: T? takeIf函数最终返回的可空的T泛型类型
*/
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? {
    contract {
        callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
    }
    //如果lambda表达式返回false,则takeUnless返回T泛型this对象本身,否则返回null
    return if (!predicate(this)) this else null
}

2. kotlin转换函数源码解析

2.1 map

  • 功能介绍:
    源码及分析注释如下:把lambda表达式的返回结果(最后一行),添加到新的集合destination中,并返回。
  • 示例代码:
fun main() {
    var list = listOf("A", "B", "C")
    list.map {
        "[$it]"
    }.map {
        "[$it, len=${it.length}]"
    }.map {
        print("$it ")
    }

}

运行结果如下:
在这里插入图片描述

  • 源码分析:
//Iterable类的扩展函数map
public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> {
    //将创建的List数组和lambda表达式传入mapTo函数
    //return this.mapTo(ArrayList<R>(collectionSizeOrDefault(10)), transform) //this可以省略
    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 {
    //遍历调用map的List对象
    for (item in this)
        //将原有的list值传递给lambda表达式transform处理,并存入新的集合destination
        destination.add(transform(item))
    return destination //返回新的集合
}

2.2 flatmap

  • 功能介绍:
    lambda表达式的返回结果是一个集合,把lambda表达式的返回集合,通过addAll函数铺开,并添加到新的集合destination中,最后返回新的集合。
  • 示例代码:
fun main() {
    var list = listOf("A", "B", "C")
    list.flatMap {
        listOf("[$it ,", "$it]")
    }.map {
        print("$it ")
    }
}

运行结果如下:
在这里插入图片描述

  • 源码分析:
//Iterable类的扩展函数
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 {
    //遍历flatMap调用者的集合元素
    for (element in this) {
        //lambda表达式返回一个集合
        val list = transform(element)
        //将lambda表达式返回的集合添加到新的集合destination中
        destination.addAll(list)
    }
    //返回新的集合
    return destination
}

2.3 filter

  • 功能介绍:
    如果lambda表达式的返回值为true,则遍历当前元素,加入新的字符串构造器StringBuilder destination,否则,不加入新的字符串。最后返回新的字符串
  • 示例代码:
fun main() {
    var list = listOf("A", "B", "C")
    list.map {
        //过滤list中为A的元素
        it.filter { it ->
            println("$it filter")
            it == 'A'
        }
    }.map {
        print("$it  ")
    }
}

运行结果如下:
在这里插入图片描述

  • 源码分析:
//String类的扩展函数filter
public inline fun String.filter(predicate: (Char) -> Boolean): String {
    return filterTo(StringBuilder(), predicate).toString()
}

public inline fun <C : Appendable> CharSequence.filterTo(destination: C, predicate: (Char) -> Boolean): C {
    //遍历filter调用者的字符串的所有字符
    for (index in 0 until length) {
        val element = get(index)
        //如果这个字符传入lambda表达式predicate的返回结果为true,则添加到StringBuilder中
        if (predicate(element)) destination.append(element)
    }
    //返回新的字符串StringBuilder
    return destination 
}

2.4 zip

  • 功能介绍
    通过lambda表达式将第一个集合和另一个集合,以Pair的形式添加到新的集合中,并返回
  • 示例代码
fun main() {
    val name = listOf("sun", "mekeater")
    val age = listOf(18,19)
    
    var zip = name.zip(age)
    //将zip转为Map遍历
    zip.toMap().forEach{
        println("name=${it.key},age=${it.value}")
    }
}

运行结果如下:
在这里插入图片描述

  • 源码分析
//Iterable类的扩展函数zip
public infix fun <T, R> Iterable<T>.zip(other: Iterable<R>): List<Pair<T, R>> {
    return zip(other) { t1, t2 -> t1 to t2 }
}
//中缀表达式,将A和B组合成一个Pair对象
public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)

public inline fun <T, R, V> Iterable<T>.zip(other: Iterable<R>, transform: (a: T, b: R) -> V): List<V> {
    //zip函数调用者对象遍历器
    val first = iterator()
    //zip函数中传入的另一个集合对象遍历器
    val second = other.iterator()
    //新建集合
    val list = ArrayList<V>(minOf(collectionSizeOrDefault(10), other.collectionSizeOrDefault(10)))
    //遍历两个集合
    while (first.hasNext() && second.hasNext()) {
        //将两个集合的元素传给lambda表达式transform
        //上面可以看到transform的实现,是将两个参数组合为一个Pair
        //将Pair对象添加到新的集合list中
        list.add(transform(first.next(), second.next()))
    }
    //返回新的集合
    return list
}

3. 函数式编程理解

kotlin是函数式编程语言,函数式编程语言的代码更简洁直观(如果你会的话,真的很容易读懂,但如果不会,那真的又一一点看不懂,相信认真看了我kotlin这7篇文章的,到这儿应该都能轻松的读懂下的kotlin代码),我们以一段示例代码来对比下,函数式编程和普通编程方式的区别。

如下一段kotlin函数式编程,用普通的java代码写,需要写一堆才能实现

fun main() {
    val name = listOf("sun", "mekeater")
    val age = listOf(18,19)
    //name.zip(age) 将name和age两个集合组成键值对集合
    //.toMap() 将上一步键值对集合转为Map集合
    //.map { "name:${it.key},age:${it.value}"} 遍历上一步Map集合中的元素,按照"name:${it.key},age:${it.value}"格式进行组合
    //.map { println(it) 遍历上一步Map集合中的元素,进行打印输出
    name.zip(age).toMap().map { "name:${it.key},age:${it.value}"}.map { println(it) }
}

对上面的kotlin函数式编程写的代码用java实现,示例代码如下:

public class Kt43_java {
    public static void main(String[] args) {
        List<String> names=new ArrayList<>();
        names.add("sun");
        names.add("mekeater");

        List<Integer> ages =new ArrayList<>();
        ages.add(18);
        ages.add(19);

        Map<String, Integer> map =new LinkedHashMap<>();
        for (int i = 0; i < names.size(); i++) {
            map.put(names.get(i), ages.get(i));
        }

        List<String> newList=new ArrayList<>();
        for (Map.Entry<String, Integer> stringIntegerEntry : map.entrySet()) {
            newList.add("name:"+stringIntegerEntry.getKey()+" age:"+stringIntegerEntry.getValue());
        }

        for (String s : newList) {
            System.out.println(s);
        }
    }

}

kotlin的一行代码,java要用一堆才能实现,kotlin代码的简洁程度不言而喻!

4. 手写RxJava

采用kotlin代码实现RxJava的create,map,observer三个操作符,通过这个示例,真的更能感受到kotlin代码的简洁程度令人发指,哈哈哈

fun main() {
    create {
        "mekeater"
    }.map {
        "[$this]"
    }.map {
        "==$this=="
    }.observer {
        println(this)
    }
}
class RxjavaCoreClass<T>(val item:T)

//lambda对输入进行处理
inline fun<I> RxjavaCoreClass<I>.observer(observerAction:I.()->Unit):Unit = observerAction(item)

//对于输入进行处理,转换为输出给到RxjavaCoreClass
inline fun<I,O> RxjavaCoreClass<I>.map(mapAction:I.()->O) = RxjavaCoreClass(mapAction(item))

//create操作符只是将用户最后一行的输入原样输出给RxjavaCoreClass,起到一个输入参数的作用
inline fun<O> create(action:()->O) = RxjavaCoreClass(action())

运行结果如下:
在这里插入图片描述

5. 结语

自此,我们kotlin入门教程系列就讲完了,一共7篇文章,如果您能从第一篇看到第七篇,相信你已经很好的掌握kotlin语言了,至少能够看懂kotlin代码,能够自己上手写kotlin代码了。

那么,感谢你来我的博客世界,听我讲kotlin的故事,这个故事该说再见了,希望在下个新故事,还能遇到你,886~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/634686.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

来盘点我的校园生活(3)

来公布上期数学题答案:12 你算对了吗&#xff1f; 今天我们班真是炸开了锅。事情是这样的&#xff0c;我今天早晨上学&#xff0c;学校不让早到&#xff0c;但我一个不小心早到了&#xff0c;主任的规定是尽量不早到&#xff0c;早到不扣分&#xff0c;倒要站在那儿背书&…

「项目」负载均衡在线OJ(ONLINE_JUDGE)系统

&#x1f436;博主主页&#xff1a;ᰔᩚ. 一怀明月ꦿ ❤️‍&#x1f525;专栏系列&#xff1a;线性代数&#xff0c;C初学者入门训练&#xff0c;题解C&#xff0c;C的使用文章&#xff0c;「初学」C&#xff0c;linux &#x1f525;座右铭&#xff1a;“不要等到什么都没有了…

使用python实现socket进行消息传输-demo

Socket 是什么 Socket 是一种在计算机网络中用于实现进程间通信的一种机制。它是网络编程中的重要概念&#xff0c;通过它可以在不同的计算机之间进行数据传输和通信。Socket 可以用于实现各种网络应用&#xff0c;包括客户端-服务器模型、P2P 应用等。基本上&#xff0c;Sock…

uniappx 应用未读角标插件(完善推送、通知、消息效果) Ba-Shortcut-Badge-U

简介&#xff08;下载地址&#xff09; Ba-Shortcut-Badge-U 是设置应用未读角标的原生插件&#xff0c;UTS版本&#xff08;同时支持uniapp和uniappx&#xff09; 支持设置未读、清空未读支持机型有Huawei、oppo、xiaomi、Samsung、Sony、LG、HTC、ZUK、ASUS、ADW、APEX、NO…

【实验指南】华为DHCP与ACL配置实战,网络新手也能轻松上手!

周六&#xff1a;网络CCNAHCIA线上直播/线下面授——同步上课 周日&#xff1a;网络CCNPHCIP线上直播/线下面授——同步上课 周六&#xff1a;系统 RHCE线上直播/线下面授——同步上课 周日&#xff1a;系统 RHCA线上直播/线下面授——同步上课 点击&#x1f447; 免费预约…

三头六臂战力增倍——openGauss5.0.0一主两备扩容至一主三备

openGauss支持集群的扩容和缩容&#xff0c;下面简单记录了openGauss5.0.0一主两备扩容至一主三备的过程&#xff0c;供各位方家参考指正。 一、检查升级前一主两备集群状态 1. 检查OS状态 root用户执行 ./gs_checkos -i A 检查结果&#xff1a;没有异常&#xff08;Abnorma…

【C++】多态(多态的原理)

在本篇博客中&#xff0c;作者将会带领你深入理解C中的多态。 声明&#xff01;&#xff01;&#xff01;本代码以及讲解都是在32位机器下进行完成的&#xff0c;64位机器下会有所不同&#xff0c;但大同小异。 一.多态的概念 什么是多态&#xff1f; 多态就是不同的对象去做…

功耗相关总结

文章目录 功耗相关的使用场景MCU中低功耗的应用RTOS中低功耗应用 功耗相关的使用场景 目前越来越多的嵌入式设备采用电池进行供电&#xff0c;而不是跟台式电脑一样&#xff0c;可以一直连接着电源。在电池供电的场景下&#xff0c;对功耗的要求很高&#xff0c;工程师们尽量希…

maven的tomcat运行不起来的解决方案

问题描述&#xff0c;出现的情况就是无法一直持续启动&#xff0c;开启后瞬间关闭。 解决方案 把maven文件中的jar换成war <packaging>jar</packaging> 换成 <packaging>war</packaging>

SpringBoot使用Mock进行单元测试

需求说明&#xff1a;需要对一个service接口进行单元测试 1.在pom.xml中加入依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-ins…

Elasticsearch8.x 向量搜索实现图搜图及文搜图

文章目录 docker 安装 es 8.x通过eland上传机器学习模型从网站上爬取图片数据将图片数据写入es中实现文搜图及图搜图 docker 安装 es 8.x version: "3"services:elasticsearch:image: docker.elastic.co/elasticsearch/elasticsearch:8.13.2environment:- discovery…

接口自动化用例怎么写?怎样设计?

一、前言 在开始接口测试之前&#xff0c;我们来想一下&#xff0c;如何进行接口测试的准备工作。或者说&#xff0c;接口测试的流程是什么&#xff1f;有些人就很好奇&#xff0c;接口测试要流程干嘛&#xff1f;不就是拿着接口文档直接利用接口测试工具测试嘛 其实&#xf…

Mia for Gmail for Mac:Mac用户的邮件管理首选

对于追求高效工作的Mac用户来说&#xff0c;Mia for Gmail for Mac无疑是邮件管理的首选工具。它以其卓越的性能和丰富的功能&#xff0c;为用户带来了前所未有的高效邮件管理体验。 Mia for Gmail for Mac不仅支持多帐号登录和标签选择功能&#xff0c;还提供了邮件分类、垃圾…

Vue3:动态路由+子页面(新增、详情页)动态路由配置(代码全注释)

文章目录 实现思路调用后端接口获取用户权限获取页面权限动态绑定到路由对象中动态添加子页面路由 实现思路 emm&#xff0c;项目中使用动态路由实现根据后端返回的用户详情信息&#xff0c;动态将该用户能够访问的页面信息&#xff0c;动态生成并且绑定到路由对象中。但是后…

必应bing国内推广开户,全方位必应广告开户流程介绍!

在所有获客渠道中&#xff0c;搜索引擎广告成为企业扩大品牌影响力、精准触达目标客户的关键途径之一。作为全球领先的搜索引擎之一&#xff0c;必应&#xff08;Bing&#xff09;拥有庞大的用户群体和独特的市场优势&#xff0c;是企业不可忽视的营销阵地。云衔科技&#xff0…

Spring Web MVC介绍及详细教程

目录 1.什么是Spring Web MVC&#xff1f; 1.1 MVC定义 1.2 Spring MVC与MVC关系 2.为什么要学习Spring MVC 3.项目创建 4.Spring MVC连接 4.1 RequestMapping 4.2 PostMapping和GetMapping 5.Spring MVC参数获取 5.1 获取单个参数 5.2 获取多个参数 5.3 获取普通对…

echart 折线图tooltip

运行结果 代码 import { truncate, merge } from lodash; import { getBasePieOptions, getTooltipFormatter } from "*/money/utils";const colorArray [#1F8BFF, #EDBE75, #26E3F0, #AF8FFF, #61DDAA, #FD996A, #8367E0, #1AAF87]export function getLineOptions…

Prompt Engineering Guide

本文转载自&#xff1a;Prompt Engineering Guide https://www.promptingguide.ai/zh/introduction/basics 文章目录 提示工程简介1、基本概念1&#xff09;基础提示词2&#xff09;提示词格式 2、提示词要素3、设计提示的通用技巧从简单开始指令具体性避免不精确做还是不做&am…

【Spring security】【pig】Note03-pig token令牌解析器过程

&#x1f338;&#x1f338; pig token令牌解析器过程 &#x1f338;&#x1f338; pig后端源码 一、解析请求中的令牌值。 二、验证令牌 内省并验证给定的令牌&#xff0c;返回其属性。返回映射表示令牌有效。 /*** author lengleng* date 2019/2/1 扩展用户信息*/ publi…

互联网医院开发:引领智慧医疗新时代

随着科技的迅猛发展和互联网的普及&#xff0c;传统医疗模式正在迎来一场深刻的变革。互联网医院的崛起&#xff0c;打破了时间和空间的限制&#xff0c;为患者和医疗机构带来了更加便捷、高效、安全的医疗服务体验。本文将从技术角度深入探讨互联网医院的开发&#xff0c;包括…