皮皮网
皮皮网

【捆绑工具源码】【sic指标源码】【源码系统后台】kotlin 源码分析

来源:ntoskrnl源码 发表时间:2024-11-27 19:42:59

1.[译]Kotlin的源码独门秘籍Reified实化类型参数(上篇)
2.Kotlin作用域函数详解
3.compileDebugKotlin FAILED和aidl
4.两个 this 一起用?Kotlin 的成员扩展函数和 implicit receiver
5.Kotlin差异化分析,let,分析run,源码with,分析apply及also
6.别扯了,源码Kotlin真是分析捆绑工具源码项目开发必备吗

kotlin 源码分析

[译]Kotlin的独门秘籍Reified实化类型参数(上篇)

       本篇已经是 Kotlin 泛型系列第三篇,我们回顾一下前面两篇:第一篇讲的源码是泛型中的类型形参和类型实参,第二篇讨论了什么时候该使用类型形参约束。分析今天,源码我们将探讨Kotlin中独有的分析一种泛型特性——实化类型参数(Reified Type Parameters)。

       历史背景:泛型在 Java 中于 JDK1.5 版本引入,源码而集合 Collection 在 JDK1.2 版本就已经存在,分析后来加入了泛型版本的源码 List。在泛型出现之前,分析我们使用原生态的源码 List 表示。由于泛型擦除的存在,无论使用 List 还是 List,它们在运行时都表现为 List 类型,泛型信息在编译阶段被抹去。伪泛型虽然存在,但它在运行时并未保持泛型特性。与之相对,C# 采用的是真泛型,没有泛型擦除问题。Kotlin 虽然力求与 Java 互操作性,但也使用了伪泛型,存在泛型擦除。不过,Kotlin 提供了实化类型参数这一特性,让运行时依然能获取到泛型的具体类型。

       实化类型参数适用于函数(或具有 get() 函数的扩展属性),且仅限于声明为 inline 内联的函数。当函数被标记为 inline 时,编译器会在每次调用处插入函数的字节码实现,从而在调用时获取具体实际类型信息。这正是实化类型参数的工作原理。

       让我们举例说明在哪些场景中应该使用泛型类型参数引用而不是真实的类名。通常情况下,当我们在源码中编写类名时,案例 1-5 是可以使用类型参数(如 T)替代 Thing 类的场景。对于案例 6-,如果要将类型参数(如 T)替换为具体类名(如 Thing 或 ExceptionalThing),最终会导致编译器错误。

       让我们详细看看实化类型参数的实际应用。以一个 User 类为例,假设我们想要反序列化一个 JSON 字符串。在 Java 的序列化库(如 Gson)中,通常需要将 Class 对象作为参数传递,以便知道想要的类型。使用实化类型参数,我们可以创建一个包装 Gson 方法的轻量级扩展函数:

       通过这种方式,我们可以在 Kotlin 代码中反序列化 JSON 字符串,而无需传递类型信息。sic指标源码Kotlin 会根据其使用方式推断出类型,例如将结果分配给 User 类型的变量时,它会使用此类型作为 fromJson() 的类型参数。这样不仅简化了代码,还避免了在不同场景中重复传递类型信息的繁琐。

       实化类型参数的使用场景还包括:简化序列化与反序列化过程、避免繁琐的类型信息传递、以及利用类型推断优化代码结构。Kotlin 的这一特性使得泛型使用更加灵活,提高了代码的可读性和效率。

       接下来,下篇译文将深入探讨实化类型参数,敬请期待。如果您对 Kotlin 技术感兴趣,欢迎加入 Kotlin 开发者联盟,获取最新技术文章和社区资源。

Kotlin作用域函数详解

       在Kotlin中,函数被赋予了与数据类型同等的公民地位,其类型可以被存放在变量中。函数类型多样,例如 ->Unit、(Int, Int)>String、Int.(String)>String 等,皆具备参数与返回值。特别是Int.(String)>String 类型,表示可以在Int对象上调用一个String参数并返回String类型,类似成员方法的使用,通过this访问接收者类型Int的对象。

       函数类型定义为变量后,Lambda表达式参数与返回值明确,类似于扩展函数,可通过1.test("2")调用,参数传递与扩展函数一致。

       作用域函数是Kotlin内置工具,用于数据操作转换。了解let、run、also、apply、repeat、with等,有助于优化代码逻辑。

       let与run功能相似,均返回Lambda执行结果,区别在于let有参数,而run无参数。run的this关键字用于访问user对象属性与函数。

       also与apply也类似,返回当前执行对象,also有参数,通过this访问属性,apply则直接使用this。源码系统后台

       repeat用于重复执行Lambda表达式,while循环等常见用法。

       with函数是顶级函数,内部Lambda无需参数,通过this访问传入对象的公开属性与函数。

       源码分析揭示了作用域函数编译后的简洁与高效,所有Lambda内部操作均直接执行,避免了匿名内部类的生成。

       反编译结果显示,let、run、also、apply、repeat、with等函数的Lambda内部操作直接呈现,inline修饰优化了性能。

       小结指出,理解Kotlin作用域函数对编程效率至关重要,其基于函数类型与扩展性特性,应用于数据操作与逻辑控制。

       感谢阅读,希望本文加深了对作用域函数的理解。课后练习:尝试默写let、run、apply、also、with、repeat,以深化认知。

compileDebugKotlin FAILED和aidl

        自从入职CS,项目编译一直有个神坑报错,每次都需要clean rebuild若干次, 非常耽误时间

        简单的说, 如果在使用AIDL时需要一个自定义的数据类型, 我们一般会这么写:

        当我们写一个子类SubClass继承该类.然后在Kotlin文件中直接或者间接引用到SubClass时, 就会出现一个以下的报错

        报错发生在 app:compileDebugKotlin , 也就是kotlinc. 但是我们明明已经定义了该类. 全局搜索发现有两个 CustomParcel.java, 推测是两个同名的文件引起.

        除了我们自己写的Java文件, 另外一个肯定是aidl生成的. 引用一张图:

        在编译开始时会把aidl转化为Java文件, 接下来才会经过javac, kotlinc把JVM语言文件转化为字节码 .class 文件.

        查看aidl生成的文件, 发现是空的, 并且有一行注释: 说明这是一个 PlaceHolder, 也就是占位文件.

        网上搜到有人遇到了 相同的问题 ,问题确实发生在kotlinC编译器以aidl生成的空java文件为编译目标, 而不是真正的java类文件. 并且也给出了解决办法,升级buildTools版本.

        查看 buildTools提交记录

        提交记录: No java output for parcelable declaration. 也就是移除了以下的为自定义的aidl Parcelable类生成Java文件的设定(.0.2之前的实现)

        升级.0.3, 再次编译, 发现build/aidl目录下不再生成同名的 PlaceHolder文件了, 只剩下唯一的我们自己的文件, kotlinC这次只能用唯一的文件来编译,报错解决.

        至于为什么有时候clean rebuild能编译成功,需要探究下kotlinC的源码.

        最坑的是, .0.2就是 gradle plugin4.1默认支持的版本 , 所以你不手动指定buildTools版本为.0.3以上就会掉进坑里.

两个 this 一起用?Kotlin 的成员扩展函数和 implicit receiver

       大家好,我是扔物线朱凯。

       今天咱说说 Kotlin 的 implicit receiver。这是一个我们写 Kotlin 经常会用的东西,虽然你可能都没听过这个词,但你一定用过它。Kotlin 的很多高级功能,都利用到了这个概念——比如协程,协程是重度依赖它的,非常重。所以,弄明白它是个什么、怎么用、怎么去发挥它最大的价值,对我们的能力提升是非常有帮助的。

       定义:其实隐式的接收器就是this。所谓的接收,其实指的就是接收调用,或者说接受调用。接受函数的调用啊,接受属性的访问啊。比如这个user.name:

       左边的user 就是它的 receiver。谁的ub源码分析 receiver?对于 name 的访问的 receiver。

       而隐式的 receiver,指的就是不用写也自动存在的 receiver。也就是如果我把这个user. 给删了,它依然能取到某个 User 对象的 name:

       那么这个隐式地被应用的User 对象,就是对这个 name 的访问的 implicit receiver,隐式的 receiver。

       嵌套的 implicit receiver 在 Kotlin 里是可以嵌套的,比如在 Java 里我们可以这么写:

       我在这个内部类的里面,想访问内部类和外部类的成员都是可以的,是吧:

       这个innerInt 是 InnerClass 里的,所以它等价于加上 this 的写法:

       而下面的outerInt 属于外面的 OuterClass,但为了避免歧义,Java 不允许我们直接写 this:

       而需要显式地加上OuterClass 的前缀:

       而上面的innerInt 如果展开,前缀是 InnerClass:

       也就是说,在内部类的里面,我是有内部类和外部类的双重this 的。对吧?

       另外,对于它们同名的成员变量或者方法,如果我也省略掉this:

       拿到的就是内部类的成员。如果想拿外部类的,就必须把this 写完整:

       Kotlin 增加的 implicit receiver 嵌套:通过函数的 receiver 指定

       然后,Kotlin 对于这种嵌套,又新增了一类场景——咱刚才看的是通过内部类来嵌套是吧?Kotlin 让我们还可以直接通过函数来嵌套新的this。比如你有一个在类型内部声明的扩展函数:

       ——这种函数叫 member extension function,成员扩展函数,其实就是字面意思:它既是成员函数又是扩展函数,对吧?

       这种「成员扩展函数」有一个问题:一方面,因为它是Int 的扩展函数,所以你需要对 Int 类型的对象才能调用它;但同时,它也是 IntMultiplier 的成员函数,所以你还要求你对 IntMultiplier 对象调用它:

       也就是说,这里需要的是个双重 receiver:既要这个直接的Int,又要那个外部的 IntMultiplier,缺一不可。——那我到底对谁调用?

       Kotlin 提供的解法是,你专门创建一个函数,并给它设置一个函数类型的参数:

       函数不用做什么特别的事,关键是执行一下它的那个函数类型的参数:

       另外,你要给这个函数类型的参数,设置一个 receiver 的类型:

       这么一指定,就把参数的函数体内部——注意,是这个block 的函数体,不是外部函数本身的函数体——在它内部强行安插了一个隐式的 receiver。换句话说,我在调用这个外部函数的时候,它的函数类型的参数的大括号里就有一个 IntMultiplier 类型的 this 了:

       通过这种写法,我们就可以任意地往代码里插入我们指定的 implicit receiver,或者说指定的this,去应对「多个 this」的需求场景了。

       协程里的应用

       Kotlin 的官方代码,以及很多第三方库,都重度地依赖这个叫做 implicit receiver 的惠泽论坛源码东西。虽然我们可以说「它不就是this 嘛」,但关键是,它给我们带来了很大的方便,怎么叫其实是次要的。随便举个例子,我们知道协程的启动是一定要用 CoroutineScope 才行的:

       但是为什么在协程的内部再启动新的协程,就不用写CoroutineScope 了?

       因为它有一个隐式的CoroutineScope 作为 this 被提供了:

       总结

       其他很多官方源码以及第三方库,都有类似的应用,而我们自己也可以在代码里用这样的写法去安插新的this 层级,或者说——安插 implicit receiver,隐式的 receiver。看起来好像很复杂,但当你明白它的这些本质逻辑,写起来就很简单了。

Kotlin差异化分析,let,run,with,apply及also

       作用域函数是Kotlin中一种重要的特性,包括let、run、with、apply以及also,这五个函数在执行方式上有相似之处,但各自存在差异。掌握这些函数的不同之处,有助于在不同场景下更好地运用它们。本文将介绍:

       Kotlin的作用域函数

       在Kotlin标准库中,存在一些函数,其唯一目的就是在对象的上下文中执行代码块。当对一个对象调用此类函数并传入一个lambda表达式时,会形成一个临时作用域。在这个作用域内,可以访问该对象,而无需使用其名称。这些函数被称为作用域函数。

       作用域函数的主要目的是为了方便对对象进行访问和操作,例如进行空检查、修改属性或直接返回值等。以下是对作用域函数的详细说明。

       2.1 let

       let函数是参数化类型T的扩展函数,在let块内可以使用it来指代该对象。返回值为let块的最后一行或指定的return表达式。

       以Book对象为例,假设其包含name和price属性,如下:

       在这个案例中,我们对Book对象使用let作用域函数,并在函数块的最后添加了字符串代码,打印出对象。可以看到,最后控制台输出的结果为字符串“This book is 《计算机网络》”。这是由于let函数的特性导致的,因为在Kotlin中,如果let块中的最后一条语句是非赋值语句,则默认情况下它是返回语句。

       如果将let块中最后一条语句修改为赋值语句,会发生什么变化?

       将Book对象的name值进行赋值操作,打印对象,但最后控制台的输出结果为“kotlin.Unit”。这是因为let函数块的最后一句是赋值语句,print将其当作一个函数看待。

       这是let角色设定的第一点:1️⃣

       关于let的第二点:2️⃣

       要对非空对象执行操作,可以使用安全调用操作符?.并调用let在lambda表达式中执行操作。如下案例:

       设置name为一个可空字符串,利用name?.let进行空判断,只有当name不为空时,逻辑才能进入let函数块中。在这里,我们可能还看不出来let空判断的优势,但当有大量name属性需要编写时,就能发现let的快速和简洁。

       关于这一点,官方教程给出了一个案例,直接使用:

       目的是获取数组列表中长度大于3的值。因为必须打印结果,所以将结果存储在一个单独的变量中,然后打印它。但使用“let”操作符,可以将代码修改为:

       使用let后,可以直接对数组列表中长度大于3的值进行打印,去掉了变量赋值这一步。

       另外,let函数还存在一个特点。

       关于这一点,let的第四点:4️⃣

       let是通过使用“It”关键字来引用对象的上下文,因此,这个“It”可以被重命名为一个可读的lambda参数,如下将it重命名为book:

       2.2 run

       run函数以“this”作为上下文对象,调用方式与let一致。

       关于run的第一点:1️⃣当lambda表达式中同时包含对象初始化和返回值的计算时,run更适合。

       这句话的意思是,如果不使用run函数,相同功能下的代码会怎样?来看一看:

       输出结果还是一样的,但run函数所带来的代码简洁程度已经显而易见。

       除此之外,让我们来看看run函数的其他优点:

       通过查看源码,了解到run函数存在两种声明方式,

       1、与let一样,run是作为T的扩展函数;

       2、第二个run的声明方式则不同,它不是扩展函数,并且块中也没有输入值,因此,它不是用于传递对象并更改属性的类型,而是可以使你在需要表达式的地方就可以执行一个语句。

       如下利用run函数块执行方法,而不是作为一个扩展函数:

       2.3 with

       with属于非扩展函数,直接输入一个对象receiver,当输入receiver后,便可以更改receiver的属性,同时,它也与run做着同样的事情。

       提供一个案例说明:

       以上面为例,with(T)类型传入了一个参数book,则可以在with的代码块中访问book的name和price属性,并做更改。

       with使用的是非null的对象,当函数块中不需要返回值时,可以使用with。

       2.4 apply

       apply是T的扩展函数,与run函数有些相似,它将对象的上下文引用为“this”而不是“it”,并提供空安全检查。不同的是,apply不接受函数块中的返回值,返回的是自己的T类型对象。

       前面看到的let、with和run函数返回的值都是R。但是,apply和下面查看的also返回T。例如,在let中,没有在函数块中返回的值,最终会成为Unit类型,但在apply中,最后返回对象本身(T)时,它成为Book类型。

       apply函数主要用于初始化或更改对象,因为它用于在不使用对象的函数的情况下返回自身。

       2.5 also

       also是T的扩展函数,返回值与apply一致,直接返回T。also函数的用法类似于let函数,将对象的上下文引用为“it”而不是“this”以及提供空安全检查方面。

       因为T作为block函数的输入,可以使用also来访问属性。所以,在不使用或不改变对象属性的情况下也使用also。

       3.1 let & run

       3.2 with & run

       with和run其实做的是同一种事情,对上下文对象都称之为“this”,但他们又存在着不同,我们来看看案例。

       先使用with函数:

       我们创建了一个可空对象book,利用with函数对book对象的属性进行了修改。代码很直观,那么我们接着将with替换为run,代码更改为:

       首先run函数的调用省略了this引用,在外层就进行了空安全检查,只有非空时才能进入函数块内对book进行操作。

       3.3 apply & let

       何时应该使用apply、with、let、also和run?总结

       以上便是Kotlin作用域函数的作用以及使用场景。在Android实际开发中,5种函数的使用频率非常高。在使用过程中发现,当代码逻辑较少时,作用域函数能带来代码的简洁性和可读性,但逻辑复杂时,使用不同的函数,多次叠加会降低可读性。这就要我们区分它们各自的特点,以便在适合且复杂的场景下去使用它们。

       最后,我整理了一些Kotlin Android相关的学习文档和面试题,希望能帮助大家学习提升。如有需要参考的,可以直接私信“1”找我参考。

别扯了,Kotlin真是项目开发必备吗

       kotlin为什么那么受欢迎。

       Kotlin相比Java其最大的优势是什么?

       你是否考虑过切换到Kotlin,它是支持Android开发的热门语言。

       它在开发人员中迅速普及背后的原因是什么?

       Kotlin 是一种在 Java 虚拟机上运行的静态类型编程语言。它主要是 JetBrains 开发团队所开发出来的编程语言。虽然 Kotlin 与 Java 语法并不兼容,但 Kotlin 被设计成可以和 Java 代码相互运作,并可以重复使用如 Java 集合框架等现有 Java 引用的方法库。它很容易在 Android 项目中替代 Java 或者同 Java 一起使用。

       如果你是一名 Android 开发,那掌握 Kotlin 语言对你就很重要了,在 Android P 源码、Gradle 源码与 Android Jetpack 库中都已大量使用了 Kotlin 语言。

       为什么要学习kotlin?与Java比又咋样?

       1.java语言比较稳健,久经考验,有庞大的生产代码库。然而,java裹足不前,开发者喜欢的很多现代语言高级特性,它都没有,或者迟迟加入。

       2.kotlin从这些经验中受益良多,而java中的某些早期设计却越来越陈旧。脱胎于旧语言,kotlin解决了很多痛点,进化成一门优秀的语言。相比java,kotlin进步巨大,带来了更可靠的开发体验。

       3.最重要的一点,google推崇kotlin,将kotlin定性为android主要开发语言,很多优秀的开源源码库均是由kotlin编写并维护。

       Java的“问题”是什么?

       1.Java是在年前发布的。因此,它一直在堆积迭代,而现在称为了它的拖累——每个新版本的Java都必须与之前的版本兼容。

       2.简而言之,与更新,更轻,更简洁,富有表现力和先进的、功能丰富的编程语言竞争,是Java面临的最大挑战。虽然,Java仍然是Android开发的官方编程语言,但Kotlin上升的速度不容置疑。Java确实开始显示其局限性。

       Kotlin有两个赢得开发者的关键优势:

       其一,它充分利用了自Java诞生以来在过去年中积累的语言设计专业知识。 其次,它充满了Java移动开发人员一直渴望的所有急需的现代功能:已经通过大型项目证明其效率的编程语言功能。

       Kotlin 语言有什么优势?

       完全兼容 Java Null safe 支持 lambda 表达式(比 Java8 更好) 支持扩展 体验一致的开发工具链

       使用 Kotlin 开发,对于安卓开发来说,主要有下面几个有利的影响:

       更少的空指针异常 更少的代码量 更快的开发速度 更一致的开发体验

       kotlin的跨平台特性

       对于 Android 开发,掌握 Kotlin 语言就很重要了。

       这本书是《Kotlin入门到精通》,它会使用Kotlin作为主要的语言来开发一个android应用。方式是通过开发一个应用来学习这门语言,而不是根据传统的结构来学习。我会在感兴趣的点停下来通过与Java1.7对比的方式讲讲Kotlin的一些概念和特性。用这种方法你就能知道它们的不同之处,并且知道哪部分语言特性可以让你提高你的工作效率。

       当你掌握了Kotlin语言的基本应用后,就该开始强化学习,进入实战环节,相信这高级Kotlin强化学习(附Demo)的正适合接下来的配套学习。其内容主要包括三个方面Kotlin入门教程、Kotlin实战避坑指南、项目实战《Kotlin Jetpack 实战》。

Kotlin 一个很厉害的 DSL 写法

       好久没写博客了,今天发几个最近在各个地方看到的一些碉堡了的 Kotlin DSL。

       本文先介绍一种 DSL 写法,再推荐几个 DSL 项目。

       超厉害的 DSL

       之前在 KotlinTest 上看到一个很牛逼的写法(我把 README 里的实例改了下):

       这其中涉及了好几个 DSL 要素。我一一列举:

       字符串后面跟一个代码块

       这个东西的原理你只要看了源码就知道了,很简单,但是你不看就是想不到(这也是我厨 Kotlin 的原因之一,它很简单, 但是可以玩出很多花样来)。

       就这样你可以利用这个 extension 写出字符串后面跟一个代码块的 DSL 。 此处使用的语言特性有:

       明白了吧。

       使用

       举个例子,把他作为一个 URL 的 utility :

       表格字面量

       就是形如

       这样的东西(上面的代码纯粹搞起耍,请不要在意)。

       这个就更简单了我觉得你们应该都知道:

       无非就是变长参数。

       形如 "(+ 1 1)" shouldBe 2 的测试

       这个也很简单,我在小标题里写的就是我在 lice 里使用的测试。

       首先,假定我们有以下测试:

       我们希望写成:

       很简单,可以有:

       就是一个中缀表达式而已。这有什么难的?

       一些特定情况

       我给我的 lice 写的测试中直接把运行字符串的那一步给包含进去了:

       看到没有,运行 lice 代码就是这么简单,还能返回最后一个表达式的值 (喂

       注意事项

       任何长得类似这样的 DSL 都有一个缺点,就是缩进膨胀(字面意思)。 Scala 为了解决这个问题, 推荐用户使用 Tab size 2 的缩进(喂。 于是我也建议读者使用 2 空格缩进。

       几个厉害的 DSL 项目

       根据推荐程度排序:

       Anko

       传送门,不说了,最强的 Kotlin DSL 框架,想必大家早已有所耳闻:

       厉害吧。这个框架是用于 Android 的,用于描述 UI 。有一点要说一下,现在的预览插件挂了。

       弹窗:

       还有一些非 UI 的吊炸天的代码块,比如异步:

       两个基于 Swing 的 DSLGensokyo

       传送门,一个刚出来的项目,它长这样:

       Swing 其实没那么垃圾,只要配上 DSL ,啥 GUI 代码都变得好看了。

       FriceEngine DSL

       这是我之前弄的那个游戏引擎的 DSL 系统, 传送门,它长这样:

       我自己觉得做的还不错。我还为它搞了个中文版,比较粗鄙,用于讽刺中文编程,请前往同项目的 README 查看。

       官方教程里的 HTML DSL

       传送门,它长这样:

       这代码也是我从 README 里面改过的,原文太瘦了,我改的胖一点。

       官方给的例子,非常给力(当时也是看这个的源码搞懂了 anko 的原理)。

       JavaFX DSL

       传送门,它长这样:

       我个人觉得很不错了已经。

       嘛。祝大家玩 Kotlin 开心。

kotlin之协程(六),协程中的 async和launch的区别以及runBlocking

       kotlin之协程(一),线程,进程,协程,协程可以替换线程吗?

        kotlin之协程(二),Kotlin协程是什么、挂起是什么、挂起的非阻塞式

        kotlin之协程(三),开始创建协程,launch,withContext

        kotlin之协程(四),协程的核心关键字suspend

        kotlin之协程(五),launch 函数以及协程的取消与超时

        kotlin之协程(七),协程中relay、yield 区别

        launch 函数定义:

        async 函数定义:

        从源码可以看出launch 和 async的唯一区别在于async的返回值

        async 返回的是 Deferred 类型,Deferred 继承自 Job 接口,Job有的它都有,增加了一个方法 await ,这个方法接收的是 async 闭包中返回的值,async 的特点是不会阻塞当前线程,但会阻塞所在协程,也就是挂起

        runBlocking 启动的协程任务会阻断当前线程,直到该协程执行结束。当协程执行结束之后,页面才会被显示出来。

        runBlocking 通常适用于单元测试的场景,而业务开发中不会用到这个函数

相关栏目:娱乐