Skip to content

為何選擇 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 的視角看待輸入程式。KSP 的 API 對 Kotlin 來說更自然,特別是對於 Kotlin 特有功能,例如頂層函式。由於 KSP 不像 kapt 那樣委派給 javac,因此它不假設 JVM 特定行為,並可能可用於其他平台。

限制

儘管 KSP 試圖成為大多數常見使用情境的簡單解決方案,但它與其他外掛解決方案相比做出了一些權衡。以下並非 KSP 的目標:

  • 檢查原始碼的表達式層級資訊。
  • 修改原始碼。
  • 與 Java 註解處理 API 達到 100% 相容性。