這是一本從基礎(chǔ)知識(shí)、設(shè)計(jì)思想、技術(shù)方案、應(yīng)用方法、實(shí)踐技巧5個(gè)維度系統(tǒng)講解Kotlin元編程,并以此大幅提升Kotlin工程師開(kāi)發(fā)水平、研發(fā)效率和開(kāi)發(fā)體驗(yàn)的著作。作者是Kotlin領(lǐng)域的資深專(zhuān)家和布道者,本書(shū)源于他對(duì)Kotlin編譯器源碼的反復(fù)研讀和大量的工程實(shí)踐,不僅細(xì)致講解了反射、程序靜態(tài)分析、 Java注解處理器、Kotlin符號(hào)處理器、Kotlin編譯器插件、元程序的開(kāi)發(fā)和調(diào)試等核心元編程技術(shù),而且詳細(xì)剖析了Jetpack Compose的編譯器插件和IntelliJ 插件、AtomicFU 的 JVM 字節(jié)碼和JavaScript代碼的生成邏輯。本書(shū)的出版打破了元編程技術(shù)資料少、門(mén)檻高的行業(yè)現(xiàn)狀。本書(shū)包含大量案例,這些案例大多來(lái)自真實(shí)的生產(chǎn)實(shí)踐,相對(duì)成熟和完善,可以作為元編程項(xiàng)目的范本。同時(shí),本書(shū)提供大量的代碼,為了提升閱讀體驗(yàn),在注釋、書(shū)寫(xiě)和排版等方面對(duì)代碼做了精心的優(yōu)化。全書(shū)的源文件均可免費(fèi)下載,讀者可以通過(guò)作者的網(wǎng)站實(shí)時(shí)與作者互動(dòng)和交流。
(1)作者背景資深:作者先后就職于騰訊和猿輔導(dǎo),是中國(guó)Kotlin社區(qū)知名布道者和技術(shù)專(zhuān)家,Google開(kāi)發(fā)者專(zhuān)家(Kotlin方向)(2)作者經(jīng)驗(yàn)豐富:作者在Kotlin領(lǐng)域有大量的項(xiàng)目實(shí)踐經(jīng)驗(yàn),對(duì)Kotlin編譯器源碼有深入研究,著有暢銷(xiāo)書(shū)《深入理解 Kotlin 協(xié)程》。(3)內(nèi)容系統(tǒng)深入:作者結(jié)合Kotlin編譯器源碼和工程實(shí)踐經(jīng)驗(yàn),從基礎(chǔ)知識(shí)、設(shè)計(jì)思想、技術(shù)方案、應(yīng)用方法、實(shí)踐技巧5個(gè)維度系統(tǒng)講解Kotlin元編程。(4)理論實(shí)戰(zhàn)兼?zhèn)洌翰粌H詳細(xì)講解了元編程的常見(jiàn)核心技術(shù),而且提供了大量來(lái)自真實(shí)生產(chǎn)環(huán)境的案例及代碼,圖文并茂。
Preface 前 言
為何寫(xiě)作本書(shū)
2018年,我受邀在“JetBrains開(kāi)發(fā)者日—2018中國(guó)巡演”活動(dòng)中做題為“如何優(yōu)雅地使用Kotlin數(shù)據(jù)類(lèi)”的分享。在準(zhǔn)備這次分享時(shí),我花了幾天時(shí)間調(diào)研為Kotlin的數(shù)據(jù)類(lèi)提供深復(fù)制能力的可行性,并給出了基于Kotlin反射和Java注解處理器(APT)的實(shí)現(xiàn)方案,也就是后來(lái)開(kāi)源的DeepCopy項(xiàng)目。當(dāng)時(shí)我嘗試過(guò)編寫(xiě)一款編譯器插件來(lái)實(shí)現(xiàn)這個(gè)需求,不過(guò)最終因?yàn)閷?duì)Kotlin編譯器的了解有限而未能如愿。
2021年,我受邀在Google開(kāi)發(fā)者社區(qū)主辦的“社區(qū)說(shuō)”活動(dòng)中做題為“Kotlin編譯器插件:我們究竟在期待什么?”的分享。這一次,我花了兩周時(shí)間初步基于Kotlin符號(hào)處理器(KSP)和編譯器插件實(shí)現(xiàn)了數(shù)據(jù)類(lèi)的深復(fù)制,整個(gè)過(guò)程充滿了探索的樂(lè)趣。
為了加深對(duì)Kotlin編譯器的認(rèn)識(shí),我基于Kotlin編譯器插件完成了可以實(shí)現(xiàn)類(lèi)似于Android的@IntDef功能的ValueDef編譯器插件。事實(shí)上,ValueDef的功能更強(qiáng)大,
@IntDef只會(huì)在代碼編寫(xiě)時(shí)提供錯(cuò)誤提示,而ValueDef除了會(huì)提供錯(cuò)誤提示以外,還會(huì)在編譯時(shí)報(bào)錯(cuò)。
與此同時(shí),隨著Kotlin符號(hào)處理器的開(kāi)源和Jetpack Compose的發(fā)布,大家對(duì)Kotlin元編程的關(guān)注度也在逐步提升,但這方面的相關(guān)資料非常少。
于是,我向機(jī)械工業(yè)出版社的楊福川老師提出了把這些內(nèi)容整理成書(shū)的想法,得到了他的肯定和支持。有了編寫(xiě)《深入理解Kotlin協(xié)程》的經(jīng)驗(yàn),我很快就正式開(kāi)始了這本書(shū)的寫(xiě)作。
本書(shū)主要特點(diǎn)
“元編程”是一個(gè)比較龐大的話題,本書(shū)主要介紹了生產(chǎn)實(shí)踐中應(yīng)用較為廣泛的反射、Java注解處理器、Kotlin符號(hào)處理器、Kotlin編譯器插件、Kotlin語(yǔ)法分析等元編程相關(guān)的內(nèi)容。
與一般的語(yǔ)法知識(shí)不同,元編程相關(guān)的內(nèi)容通常較為抽象。為了更好地讓讀者理解元編程相關(guān)的各項(xiàng)技術(shù),本書(shū)提供了豐富的應(yīng)用案例。這些案例相對(duì)成熟和完善,可以作為元編程項(xiàng)目的范本。
本書(shū)基本上遵循了基礎(chǔ)知識(shí)介紹和案例實(shí)踐的結(jié)構(gòu)。以第3章為例,3.1節(jié)和3.2節(jié)系統(tǒng)地介紹了Java反射和Kotlin反射的概念和使用方法,是基礎(chǔ)知識(shí)介紹部分;3.3~3.5節(jié)通過(guò)案例進(jìn)一步介紹反射的適用場(chǎng)景,是案例實(shí)踐部分。
本書(shū)的實(shí)踐案例通常包括案例背景、需求分析和案例實(shí)現(xiàn)這幾方面。
案例背景:介紹案例的需求背景。本書(shū)的案例大多源自真實(shí)的生產(chǎn)實(shí)踐,因此案例背景的介紹有非常重要的價(jià)值。
需求分析:明確需求的細(xì)節(jié),拆解需求并轉(zhuǎn)換成技術(shù)方案。
案例實(shí)現(xiàn):提供詳細(xì)的問(wèn)題解決思路以及案例實(shí)現(xiàn)步驟。
在系統(tǒng)介紹了常見(jiàn)的Kotlin元編程技術(shù)之后,本書(shū)還對(duì)Jetpack Compose的編譯器插件和IntelliJ插件、AtomicFU的字節(jié)碼JavaScript代碼邏輯做了詳細(xì)的剖析。
與絕大多數(shù)技術(shù)書(shū)類(lèi)似,本書(shū)包含了大量代碼。為了提升閱讀體驗(yàn),我在編寫(xiě)本書(shū)時(shí)對(duì)代碼做了以下優(yōu)化:
省略不必要的部分,避免代碼冗長(zhǎng)而浪費(fèi)篇幅。
代碼縮進(jìn)為2個(gè)空格,以降低縮進(jìn)對(duì)閱讀體驗(yàn)的影響。
核心代碼注釋覆蓋率不低于30%,方便讀者快速理解代碼的含義。
核心代碼單行長(zhǎng)度不超過(guò)80個(gè)字符,避免排版后出現(xiàn)折行的問(wèn)題。
在部分代碼清單的開(kāi)始處標(biāo)注其所在的模塊、文件或者函數(shù)等信息,方便讀者自行查找相關(guān)源代碼。
代碼字體采用JetBrains Mono,該字體由Kotlin項(xiàng)目團(tuán)隊(duì)所屬公司JetBrains為開(kāi)發(fā)者專(zhuān)門(mén)打造,更適合代碼的閱讀。
本書(shū)閱讀對(duì)象
本書(shū)探討的內(nèi)容有一定的復(fù)雜度。在閱讀本書(shū)之前,讀者需要對(duì)Kotlin語(yǔ)言的語(yǔ)法有較為深入的理解,也需要具備一定的編譯原理的基礎(chǔ)知識(shí)。
本書(shū)適用于有一定基礎(chǔ)的Kotlin開(kāi)發(fā)者,包括但不限于正在使用和希望使用Kotlin開(kāi)發(fā)Android、Web服務(wù)、iOS、前端等應(yīng)用的開(kāi)發(fā)者。
本書(shū)非常適用于希望在Kotlin相關(guān)開(kāi)發(fā)領(lǐng)域?qū)崿F(xiàn)進(jìn)階的讀者。本書(shū)介紹的內(nèi)容對(duì)讀者提升自身編程水平以及團(tuán)隊(duì)提升研發(fā)效率都有非常大的參考價(jià)值。
本書(shū)不會(huì)介紹Kotlin的基礎(chǔ)語(yǔ)法,因此建議Kotlin初學(xué)者先閱讀相關(guān)基礎(chǔ)書(shū)。
如何閱讀本書(shū)
本書(shū)基于Kotlin 1.8.0系統(tǒng)地介紹了Kotlin元編程的基本概念、技術(shù)方案、應(yīng)用場(chǎng)景和實(shí)踐技巧。
本書(shū)主要分為三部分,分別介紹如下。
第一部分為元編程的基礎(chǔ)知識(shí)(第1章和第2章),為后續(xù)的元編程實(shí)踐提供知識(shí)儲(chǔ)備。如果讀者有一定的Kotlin元編程基礎(chǔ),可以直接閱讀第二部分內(nèi)容。在閱讀過(guò)程中,如果遇到概念相關(guān)的問(wèn)題,也可以隨時(shí)翻閱這部分內(nèi)容。
第二部分為元編程的技術(shù)實(shí)踐(第3~8章),涉及運(yùn)行時(shí)的反射、源代碼生成、編譯時(shí)的符號(hào)處理、程序靜態(tài)分析、編譯器插件、元程序的開(kāi)發(fā)和調(diào)試等內(nèi)容。這部分的章節(jié)安排相對(duì)獨(dú)立,讀者可以根據(jù)自己的實(shí)際需求選擇閱讀相應(yīng)的章節(jié)。需要說(shuō)明的是,DeepCopy項(xiàng)目是貫穿這部分內(nèi)容的綜合案例,在介紹每一種元編程技術(shù)方案時(shí),我們都會(huì)給出DeepCopy項(xiàng)目中對(duì)應(yīng)的技術(shù)方案的實(shí)現(xiàn),希望能夠幫助讀者加深對(duì)不同的元編程技術(shù)方案的認(rèn)識(shí)。在了解了元編程的常見(jiàn)技術(shù)之后,本書(shū)在第8章重點(diǎn)介紹了元編程項(xiàng)目實(shí)踐中編寫(xiě)單元測(cè)試和集成測(cè)試的常見(jiàn)方法與技巧,以提升讀者開(kāi)發(fā)元編程項(xiàng)目的
效率
目 錄 Contents
前言
第一部分 元編程的基礎(chǔ)知識(shí)
第1章 元編程概述2
1.1 元編程的需求背景2
1.2 元編程的基本概念4
1.2.1 元編程的定義5
1.2.2 元編程的分類(lèi)5
1.3 元編程的學(xué)習(xí)方法6
1.3.1 培養(yǎng)興趣6
1.3.2 付諸行動(dòng)6
1.3.3 善用工具7
1.3.4 多讀源代碼8
1.4 常用項(xiàng)目的調(diào)試環(huán)境配置8
1.4.1 Java編譯器8
1.4.2 Kotlin編譯器11
1.4.3 IntelliJ社區(qū)版13
1.4.4 Jetpack Compose編譯器插件19
1.5 本章小結(jié)21
第2章 元數(shù)據(jù)概述22
2.1 基本概念22
2.1.1 語(yǔ)法結(jié)構(gòu)23
2.1.2 編譯產(chǎn)物23
2.2 注釋23
2.2.1 注釋的結(jié)構(gòu)化23
2.2.2 文檔生成24
2.3 注解25
2.3.1 注解的概念25
2.3.2 源代碼可見(jiàn)的注解26
2.3.3 二進(jìn)制可見(jiàn)的注解27
2.3.4 運(yùn)行時(shí)可見(jiàn)的注解30
2.4 Kotlin的元數(shù)據(jù)31
2.4.1 Kotlin JVM中的@Metadata
注解31
2.4.2 Kotlin JVM模塊中的元數(shù)據(jù)35
2.4.3 klib中的元數(shù)據(jù)37
2.5 Kotlin的語(yǔ)法樹(shù)39
2.5.1 Kotlin的語(yǔ)法定義40
2.5.2 基于IntelliJ平臺(tái)接口的抽象語(yǔ)
法樹(shù)41
2.5.3 新一代語(yǔ)法樹(shù)FIR42
2.5.4 連接前后端編譯器的IR43
2.5.5 Java和Kotlin的符號(hào)樹(shù)45
2.6 Kotlin的編譯產(chǎn)物47
2.6.1 JVM47
2.6.2 JavaScript48
2.6.3 Native48
2.7 本章小結(jié)49
第二部分 元編程的技術(shù)實(shí)踐
第3章 運(yùn)行時(shí)的反射52
3.1 Java反射52
3.1.1 基本功能52
3.1.2 解除訪問(wèn)限制53
3.1.3 動(dòng)態(tài)代理54
3.1.4 對(duì)注解的支持55
3.1.5 對(duì)方法參數(shù)名的支持56
3.1.6 訪問(wèn)Kotlin代碼57
3.2 Kotlin反射58
3.2.1 基本功能59
3.2.2 類(lèi)引用的獲取61
3.2.3 屬性引用和函數(shù)引用65
3.2.4 typeOf67
3.2.5 dynamic類(lèi)型69
3.2.6 屬性委托70
3.3 案例:Retrofit的接口實(shí)現(xiàn)72
3.3.1 Retrofit基本用法72
3.3.2 GitHubService實(shí)例的創(chuàng)建73
3.3.3 函數(shù)參數(shù)與請(qǐng)求參數(shù)的
對(duì)應(yīng)關(guān)系74
3.3.4 泛型類(lèi)型的反序列化74
3.3.5 案例小結(jié)75
3.4 案例:使用反射實(shí)現(xiàn)DeepCopy75
3.4.1 案例背景75
3.4.2 需求分析76
3.4.3 案例實(shí)現(xiàn)78
3.4.4 小試牛刀79
3.4.5 案例小結(jié)79
3.5 案例:使用dynamic類(lèi)型為
Kotlin JS實(shí)現(xiàn)DeepCopy80
3.5.1 案例背景80
3.5.2 需求分析80
3.5.3 案例實(shí)現(xiàn)83
3.5.4 案例小結(jié)83
3.6 本章小結(jié)84
第4章 源代碼生成85
4.1 直接輸出目標(biāo)代碼85
4.1.1 一個(gè)簡(jiǎn)單的例子85
4.1.2 標(biāo)準(zhǔn)庫(kù)的代碼生成87
4.2 案例:為Kotlin添加Tuple類(lèi)型88
4.2.1 案例背景88
4.2.2 需求分析90
4.2.3 案例實(shí)現(xiàn)91
4.3 使用模板引擎生成目標(biāo)代碼93
4.3.1 Anko中的代碼生成93
4.3.2 使用模板引擎渲染目標(biāo)代碼95
4.4 案例:為Java靜態(tài)方法生成
Kotlin擴(kuò)展函數(shù)(模板引擎)96
4.4.1 案例背景96
4.4.2 需求分析96
4.4.3 案例實(shí)現(xiàn)98
4.4.4 代碼優(yōu)化101
4.5 使用代碼生成框架生成目標(biāo)代碼104
4.5.1 JavaPoet104
4.5.2 KotlinPoet109
4.6 案例:為Java靜態(tài)方法生成
Kotlin擴(kuò)展函數(shù)(KotlinPoet)114
4.6.1 類(lèi)型的映射114
4.6.2 實(shí)現(xiàn)代碼生成116
4.6.3 泛型參數(shù)的支持118
4.7 本章小結(jié)121
第5章 編譯時(shí)的符號(hào)處理122
5.1 符號(hào)的基本概念122
5.1.1 Java的符號(hào)122
5.1.2 Kotlin的符號(hào)124
5.1.3 符號(hào)與語(yǔ)法樹(shù)節(jié)點(diǎn)的關(guān)系和
區(qū)別125
5.2 處理器的基本結(jié)構(gòu)125
5.2.1 APT的基本結(jié)構(gòu)125
5.2.2 KSP的基本結(jié)構(gòu)130
5.2.3 APT與KSP的結(jié)構(gòu)差異131
5.2.4 處理器的配置文件132
5.3 深入理解符號(hào)和類(lèi)型132
5.3.1 獲取修飾符133
5.3.2 通過(guò)名稱獲取符號(hào)133
5.3.3 獲取符號(hào)的類(lèi)型134
5.3.4 通過(guò)類(lèi)型獲取符號(hào)138
5.3.5 判斷類(lèi)型之間的關(guān)系139
5.3.6 獲取注解及其參數(shù)值141
5.4 案例:基于源代碼生成模塊的
符號(hào)文件144
5.4.1 案例背景144
5.4.2 案例實(shí)現(xiàn):APT版本145
5.4.3 案例實(shí)現(xiàn):KSP版本147
5.5 深入理解符號(hào)處理器148
5.5.1 如何使用APT處理Kotlin
符號(hào)148
5.5.2 符號(hào)的有效性驗(yàn)證150
5.5.3 處理器的輪次和符號(hào)的延遲
處理150
5.5.4 處理器對(duì)增量編譯的支持151
5.5.5 多模塊的符號(hào)處理154
5.6 案例:使用符號(hào)處理器實(shí)現(xiàn)
DeepCopy156
5.6.1 案例背景156
5.6.2 需求分析156
5.6.3 案例實(shí)現(xiàn):APT版本157
5.6.4 案