為什麼選擇 KSP
編譯器外掛程式是強大的元程式設計工具,可以大幅增強您編寫程式碼的方式。編譯器外掛程式直接將編譯器作為程式庫呼叫,以分析和編輯輸入程式。這些外掛程式還可以針對各種用途產生輸出。例如,它們可以產生樣板程式碼,甚至可以為特別標記的程式元素(如 Parcelable)產生完整的實作。外掛程式還有多種其他用途,甚至可以用於實作與微調語言中未直接提供的功能。
雖然編譯器外掛程式功能強大,但這種力量是有代價的。要編寫即使是最簡單的外掛程式,您也需要具備一些編譯器背景知識,並對特定編譯器的實作細節有一定的熟悉程度。另一個實際問題是,外掛程式通常與特定的編譯器版本緊密綁定,這意味著每當您想要支援較新版本的編譯器時,可能都需要更新外掛程式。
KSP 讓建立輕量級編譯器外掛程式更容易
KSP 旨在隱藏編譯器的變更,從而最大限度地減少使用它的處理器的維護工作。KSP 的設計不與 JVM 綁定,以便將來能更輕鬆地適應其他平台。KSP 的設計還旨在最大限度地縮短建置時間。對於某些處理器,例如 Glide,與 kapt 相比,KSP 可將完整編譯時間減少多達 25%。
KSP 本身是作為編譯器外掛程式實作的。Google 的 Maven 存儲庫中有預先建置的軟體包,您可以下載並使用,而無需自行建置專案。
與 kotlinc 編譯器外掛程式的比較
kotlinc 編譯器外掛程式可以存取來自編譯器的幾乎所有內容,因此具有最大的能力和靈活性。另一方面,由於這些外掛程式可能依賴於編譯器中的任何內容,因此它們對編譯器的變更非常敏感,需要頻繁維護。這些外掛程式還需要對 kotlinc 的實作有深入的了解,因此學習曲線可能很陡峭。
KSP 旨在透過定義良好的 API 隱藏大多數編譯器變更,儘管編譯器甚至 Kotlin 語言的重大變更可能仍需要向 API 使用者公開。
KSP 試圖透過提供一個以能力換取簡潔性的 API 來滿足常見的使用案例。它的功能是通用 kotlinc 外掛程式的一個嚴格子集。例如,雖然 kotlinc 可以檢查運算式與陳述式,甚至可以修改程式碼,但 KSP 不能。
雖然編寫 kotlinc 外掛程式可能很有趣,但也可能耗費大量時間。如果您不打算學習 kotlinc 的實作,且不需要修改原始碼或讀取運算式,那麼 KSP 可能是一個不錯的選擇。
與反射的比較
KSP 的 API 看起來與 kotlin.reflect 相似。兩者之間的主要區別在於 KSP 中的型別參照需要明確解析。這也是介面不共用的原因之一。
與 kapt 的比較
kapt 是一個卓越的解決方案,它使大量的 Java 註解處理器能夠開箱即用地用於 Kotlin 程式。KSP 相對於 kapt 的主要優勢在於提升了建置效能、不綁定 JVM、更道地的 Kotlin API,以及理解僅限 Kotlin 符號的能力。
為了在不經修改的情況下執行 Java 註解處理器,kapt 將 Kotlin 程式碼編譯為 Java 虛設常式,這些虛設常式保留了 Java 註解處理器感興趣的資訊。為了產生這些虛設常式,kapt 需要解析 Kotlin 程式中的所有符號。虛設常式的產生成本大約佔完整 kotlinc 分析的 1/3,且與 kotlinc 程式碼產生的成本處於同一數量級。對於許多註解處理器來說,這比花在處理器本身的時間長得多。例如,Glide 只查看極少數具有預定義註解的類別,且其程式碼產生相當快。幾乎所有的建置開銷都存在於虛設常式產生階段。切換到 KSP 將立即減少 25% 的編譯器耗時。
為了進行效能評估,我們在 KSP 中實作了一個 簡化版本 的 Glide,使其為 Tachiyomi 專案產生程式碼。在我們的測試裝置上,雖然該專案的 Kotlin 總編譯時間為 21.55 秒,但 kapt 花了 8.67 秒產生程式碼,而我們的 KSP 實作僅花了 1.15 秒產生程式碼。
與 kapt 不同,KSP 中的處理器不會從 Java 的角度看待輸入程式。API 對 Kotlin 來說更自然,特別是對於像頂層函式這類 Kotlin 特有的功能。因為 KSP 不像 kapt 那樣委派給 javac,所以它不會假設 JVM 特定的行為,並且有可能用於其他平台。
限制
雖然 KSP 試圖成為大多數常見使用案例的簡單解決方案,但與其他外掛程式解決方案相比,它做出了一些權衡。以下不是 KSP 的目標:
- 檢查原始碼的運算式層級資訊。
- 修改原始碼。
- 與 Java 註解處理 API 保持 100% 的相容性。
