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