[譯]記一次Kotlin官方文檔翻譯的PR(內聯類)

簡述: 這幾天忽然沒更新文章了,可能有的小夥伴認爲寒冬將至,是否是認爲我跑路了(哈哈,確實不是哈,這幾天感冒挺厲害的,再加上前幾天連續熬夜寫文章,感受快扛不住了,因此暫時休息停更了一週。這不這篇內聯類官網文檔的翻譯,已經拖了不少天,今天總算給中文社區的大佬提了PR)。有些小夥伴在公衆號上留言問我協程文章何時出。這幾天正在寫協程相關的文章了,很快就能夠發佈了哈。html

翻譯說明:git

原標題: inline-classgithub

原文地址: Kotlin官網web

譯文地址: Kotlin中文站-內聯類數組

內聯類

內聯類僅在 Kotlin 1.3 以後版本可用,目前仍是實驗性的。關於詳情請參見下文性能優化

有時候,業務邏輯須要圍繞某種類型建立包裝器。然而,因爲額外的堆內存分配問題,它會引入運行時的性能開銷。此外,若是被包裝的類型是原生類型,性能的損失是很糟糕的,由於原生類型一般在運行時就進行了大量優化,然而他們的包裝器卻沒有獲得任何特殊的處理。app

爲了解決這類問題,Kotlin引入了一種被稱爲 內聯類 的特殊類,它經過在類的前面定義一個 inline 修飾符來聲明:maven

inline class Password(val value: String)
複製代碼

內聯類必須含有惟一的一個屬性在主構造函數中初始化。在運行時,將使用這個惟一屬性來表示內聯類的實例 (關於運行時的內部表達請參閱 下文):ide

// 不存在 'Password' 類的真實實例對象
// 在運行時,'securePassword' 僅僅包含 'String'
val securePassword = Password("Don't try this in production") 
複製代碼

這就是內聯類的主要特性,它靈感來源於 「inline」 這個名稱:類的數據被 「內聯」到該類使用的地方(相似於 內聯函數 中的代碼被內聯到該函數調用的地方)。函數

成員

內聯類支持普通類中的一些功能。特別是,內聯類能夠聲明屬性與函數:

inline class Name(val s: String) {
    val length: Int
        get() = s.length

    fun greet() {
        println("Hello, $s")
    }
}    

fun main() {
    val name = Name("Kotlin")
    name.greet() // `greet` 方法會做爲一個靜態方法被調用
    println(name.length) // 屬性的 get 方法會做爲一個靜態方法被調用
}
複製代碼

然而,內聯類的成員也有一些限制:

  • 內聯類不能含有 init 代碼塊
  • 內聯類不能含有 inner
  • 內聯類不能含有幕後字段
    • 所以,內聯類只能含有簡單的計算屬性(不能含有延遲初始化/委託屬性)

繼承

內聯類容許去繼承接口

interface Printable {
    fun prettyPrint(): String
}

inline class Name(val s: String) : Printable {
    override fun prettyPrint(): String = "Let's $s!"
}    

fun main() {
    val name = Name("Kotlin")
    println(name.prettyPrint()) // 仍然會做爲一個靜態方法被調用
}
複製代碼

禁止內聯類參與到類的繼承關係結構中。這就意味着內聯類不能繼承其餘的類並且必須是 final

表示方式

在生成的代碼中,Kotlin編譯器爲每一個內聯類保留一個包裝器。內聯類的實例能夠在運行時表示爲包裝器或者基礎類型。這就相似於 Int 能夠表示爲原生類型 int 或者包裝器 Integer

爲了生成性能最優的代碼,Kotlin 編譯更傾向於使用基礎類型而不是包裝器。 然而,有時候使用包裝器是必要的。通常來講,只要將內聯類用做另外一種類型,它們就會被裝箱。

interface I

inline class Foo(val i: Int) : I

fun asInline(f: Foo) {}
fun <T> asGeneric(x: T) {}
fun asInterface(i: I) {}
fun asNullable(i: Foo?) {}

fun <T> id(x: T): T = x

fun main() {
    val f = Foo(42) 
    
    asInline(f)    // 拆箱操做: 用做 Foo 自己
    asGeneric(f)   // 裝箱操做: 用做泛型類型 T
    asInterface(f) // 裝箱操做: 用做類型 I
    asNullable(f)  // 裝箱操做: 用做不一樣於 Foo 的可空類型 Foo?
    
    // 在下面這裏例子中,'f' 首先會被裝箱(當它做爲參數傳遞給 'id' 函數時)而後又被拆箱(當它從'id'函數中被返回時)
    // 最後, 'c' 中就包含了被拆箱後的內部表達(也就是 '42'), 和 'f' 同樣 
    val c = id(f)  
}
複製代碼

由於內聯類既能夠表示爲基礎類型有能夠表示爲包裝器,引用相等對於內聯類而言毫無心義,所以這也是被禁止的。

名字修飾

因爲內聯類被編譯爲其基礎類型,所以可能會致使各類模糊的錯誤,例如意想不到的平臺簽名衝突:

inline class UInt(val x: Int)

// 在JVM平臺上被表示爲'public final void compute(int x)'
fun compute(x: Int) { }

// 同理,在JVM平臺上也被表示爲'public final void compute(int x)'!
fun compute(x: UInt) { }
複製代碼

爲了緩解這種問題,通常會經過在函數名後面拼接一些穩定的哈希碼來重命名函數。 所以, fun compute(x: UInt) 將會被表示爲 public final void compute-<hashcode>(int x), 以此來解決衝突的問題。

請注意在Java中 - 是一個 無效的 符號,也就是說在Java中不能調用使用內聯類做爲形參的函數。

內聯類與類型別名

初看起來,內聯相似乎與類型別名很是類似。實際上,二者彷佛都引入了一種新的類型,而且都在運行時表示爲基礎類型。

然而,關鍵的區別在於類型別名與其基礎類型(以及具備相同基礎類型的其餘類型別名)是 賦值兼容 的,而內聯類卻不是這樣。

換句話說,內聯類引入了一個真實的新類型,與類型別名正好相反,類型別名僅僅是爲現有的類型取了個新的替代名稱(別名):

typealias NameTypeAlias = String
inline class NameInlineClass(val s: String)

fun acceptString(s: String) {}
fun acceptNameTypeAlias(n: NameTypeAlias) {}
fun acceptNameInlineClass(p: NameInlineClass) {}

fun main() {
    val nameAlias: NameTypeAlias = ""
    val nameInlineClass: NameInlineClass = NameInlineClass("")
    val string: String = ""

    acceptString(nameAlias) // 正確: 傳遞別名類型的實參替代函數中基礎類型的形參
    acceptString(nameInlineClass) // 錯誤: 不能傳遞內聯類的實參替代函數中基礎類型的形參

    // And vice versa:
    acceptNameTypeAlias("") // 正確: 傳遞基礎類型的實參替代函數中別名類型的形參
    acceptNameInlineClass("") // 錯誤: 不能傳遞基礎類型的實參替代函數中內聯類類型的形參
}
複製代碼

內聯類的實驗性狀態

內聯類的設計目前是實驗性的,這就是說此特性是正在 快速變化的,而且不保證其兼容性。在 Kotlin 1.3+ 中使用內聯類時,將會獲得一個警告,來代表此特性仍是實驗性的。

要想移除警告,你必須經過對 kotlinc 指定 -XXLanguage:+InlineClasses參數來選擇使用該實驗性的特性。

在Gradle中啓用內聯類:

compileKotlin {
    kotlinOptions.freeCompilerArgs += ["-XXLanguage:+InlineClasses"]
}
複製代碼

關於詳細信息,請參見編譯器選項。關於多平臺項目的設置,請參見使用Gradle構建多平臺項目章節。

在Maven中啓用內聯類

<configuration>
    <args>
        <arg>-XXLanguage:+InlineClasses</arg> 
    </args>
</configuration>
複製代碼

關於詳細信息,請參見指定編譯器選項

進一步討論

關於其餘技術詳細信息和討論,請參見內聯類的語言提議

譯者有話說

爲何要翻譯官方文檔,其實只想向你們傳遞一個信息: Kotlin的官方文檔確實不錯,也許學會看官方文檔才能讓你走得更快和更遠。咱們都知道學習一個新的技術,最好的方式就是看官方文檔。 爲何說Kotlin官方文檔不錯,以這次內聯類舉例,是否是還記得以前我翻譯幾篇國外大佬有關kotlin內聯類的文章,能夠說把內聯類研究得很全面了,好比內聯類自動裝箱和性能優化,內聯類與typealias類型別名的區別等等,能夠看下其實這些官網都有提到。因此國外大佬無非也是在官網基礎上對某個問題進行更深刻挖掘和探索。因此多看技術官方文檔,能讓你對某項技術瞭解的更加全面。

問題來了,可能不少人都厭倦看英文了,因此此次給你們安利一波Kotlin官方認準的Kotlin中文站、Kotlin中文博客、Kotlin中文社區

Kotlin中文站

Kotlin中文博客

Kotlin中文社區

很是歡迎熱愛Kotlin的小夥伴們加入到Kotlin中文社區,若是你想爲Kotlin中文站翻譯文檔提PR的話。你能夠直接在中文站翻譯github地址,找到灰藍天際大佬便可。固然你能夠直接到個人下面公衆號(Kotlin開發者聯盟)留言,給你引薦一波,哈哈。 若是你以爲翻譯不錯,歡迎給個star。 私人GitHub,通常人不告訴他

歡迎關注Kotlin開發者聯盟,這裏有最新Kotlin技術文章,每週會不按期翻譯一篇Kotlin國外技術文章。若是你也喜歡Kotlin,歡迎加入咱們~~~

Kotlin系列文章,歡迎查看:

Effective Kotlin翻譯系列

原創系列:

翻譯系列:

實戰系列:

相關文章
相關標籤/搜索