Kotlin 宣布一個(gè)重磅特性

Kotlin 1.5 宣布了一個(gè)重磅特性 value class不翩,這是一個(gè)非常實(shí)用的特性齿诞,提高代碼的可讀性同時(shí)衡便,還可以提高性能献起,因?yàn)榫幾g器會(huì)對(duì)它進(jìn)行更深層次的優(yōu)化洋访,減少對(duì)象的創(chuàng)建。

隨著 Kotlin 不斷的完善谴餐,出現(xiàn)了一系列的特性 inner class 姻政、 data classsealed class 岂嗓、sealed interface 汁展、inline classvalue class 等等厌殉,之前寫過幾篇文章專門分析 sealed classsealed interface善镰,可以點(diǎn)擊下方鏈接前去查看。

而今天這篇文章主要分析 inline classvalue class炫欺。

通過這篇文章你將學(xué)習(xí)到以下內(nèi)容:

  • 什么是內(nèi)聯(lián)函數(shù)?
    • 什么情況下使用內(nèi)聯(lián)函數(shù)熏兄?
  • 什么是 inline class 品洛?
  • 什么是 value class
    • value class 不能被繼承
    • value class 可以實(shí)現(xiàn)接口
    • 當(dāng)傳遞的對(duì)象為空時(shí)摩桶,會(huì)失去內(nèi)聯(lián)效果
    • value class 禁止使用 === 可以使用 ==
  • inline classvalue class 有什么區(qū)別桥状?
  • Java 如何調(diào)用接受內(nèi)聯(lián)類的函數(shù)?

內(nèi)聯(lián)函數(shù)

在 Kotlin 中通過 inline-functions (內(nèi)聯(lián)函數(shù)) 實(shí)現(xiàn)函數(shù)內(nèi)聯(lián)硝清,內(nèi)聯(lián)的作用:提升運(yùn)行效率辅斟,調(diào)用被 inline 修飾符的函數(shù),會(huì)把方法體內(nèi)的代碼放到調(diào)用的地方芦拿,其主要目的提高性能士飒,減少對(duì)象的創(chuàng)建。內(nèi)聯(lián)函數(shù)代碼如下所示蔗崎。

fun testInline() {
    printByteCode()
}

inline fun printByteCode() {
    println("printByteCode")
}

// 編譯之后
public static final void testInline() {
  String var1 = "printByteCode";
  System.out.println(var1);
}

inline 修飾的函數(shù)適用于以下情況

  • inline 修飾符適用于把函數(shù)作為另一個(gè)函數(shù)的參數(shù)酵幕,例如高階函數(shù) filtermap 缓苛、 joinToString 或者一些獨(dú)立的函數(shù) repeat
  • inline 操作符適合和 reified 操作符結(jié)合在一起使用
  • 如果函數(shù)體很短芳撒,使用 inline 操作符提高效率

如果使用 inline 修飾符標(biāo)記普通函數(shù),Android Studio 會(huì)給一個(gè)大大大的警告未桥,如下圖所示笔刹,這是為了防止 inline 操作符濫用而帶來的性能損失。

但是如果函數(shù)體很短冬耿,想通過 inline 操作符提高效率舌菜,又想消除掉警告,可以前往查看 為數(shù)不多的人知道的 Kotlin 技巧及解析(三) 文章中的 「Kotlin 注解在項(xiàng)目中的使用」淆党。

代碼示例倉庫地址:https://github.com/hi-dhl/KtKit

內(nèi)聯(lián)類

內(nèi)聯(lián)類是一個(gè)被忽略酷师,非常有用的特性。有時(shí)必要的業(yè)務(wù)邏輯染乌,需要將基本數(shù)據(jù)類型山孔、String 等等參數(shù)封裝在一個(gè) Model 中,然后在 Model 中封裝一些方法荷憋,對(duì)這個(gè)參數(shù)做檢查台颠、驗(yàn)證等等操作。

參數(shù)被封裝之后勒庄,需要?jiǎng)?chuàng)建包裝對(duì)象串前,對(duì)象的創(chuàng)建在堆中進(jìn)行分配,數(shù)據(jù)量很大的情況实蔽,對(duì)性能的損耗也非常大荡碾,例如:內(nèi)存的占用,運(yùn)行時(shí)的效率局装,頻繁創(chuàng)建對(duì)象坛吁,導(dǎo)致 GC 回收大量對(duì)象帶來的卡頓問題等等。

基本數(shù)據(jù)類型铐尚、String 等等運(yùn)行時(shí) JVM 會(huì)對(duì)它進(jìn)行優(yōu)化拨脉,但是如果將這些參數(shù)封裝在一個(gè)類中,包裝類不會(huì)做任何處理宣增,依然會(huì)在堆中創(chuàng)建對(duì)象玫膀。

所以為了減少性能的損耗,避免對(duì)象的創(chuàng)建爹脾,因此 Kotlin 推出了一個(gè)內(nèi)聯(lián)類 inline-classes帖旨。內(nèi)聯(lián)類只能在構(gòu)造函數(shù)中傳入一個(gè)參數(shù),參數(shù)需要用 val 聲明灵妨,編譯之后碉就,會(huì)替換為傳進(jìn)去的值,代碼如下所示闷串。

inline class User(val name: String)

fun testInline() {
    println(User("DHL"))
}

// 編譯之后
public static final void testInline() {
  System.out.println("DHL");
}

正如你所見瓮钥,編譯后的 Java 代碼并沒有創(chuàng)建額外的對(duì)象,在 Kotlin 中創(chuàng)建的對(duì)象被替換為傳進(jìn)去的值烹吵。

Java 如何調(diào)用接受內(nèi)聯(lián)類的函數(shù)

在 Kotlin 中聲明一個(gè)函數(shù)碉熄,并且將 inline class 作為參數(shù)傳遞,編譯成 Java 代碼之后肋拔,函數(shù)名稱會(huì)被打亂锈津,代碼如下所示。

inline class User(val name: String) 

fun login(user: User) {
}

// 編譯后的代碼
public static final void login_FY_U7ig/* $FF was: login-FY_U7ig*/(@NotNull String user) {
}

編譯后的函數(shù)名稱會(huì)被打亂凉蜂,例如 login-FY_U7ig琼梆。 但是這樣存在一個(gè)問題性誉, Java 無法調(diào)用接受內(nèi)聯(lián)類的函數(shù),代碼如下所示茎杂。

inline class User(val name: String) 

fun login(user: User) {
}

fun login(passwd: String) {
}

// Kotlin 編譯正常
login(User("DHL"))
login("ByteCode")

// Java 編譯失敗
MainKt.login("DHL");

在 Kotlin 中調(diào)用错览,可以正常編譯,因?yàn)閮?nèi)聯(lián)的原因煌往,導(dǎo)致 Java 調(diào)用就會(huì)失敗倾哺,如下圖所示。

為了能夠在 Java 中正常調(diào)用刽脖,因此添加了注解 @JvmName 更改函數(shù)名稱羞海,來解決這個(gè)問題,代碼如下所示曲管。

inline class User(val name: String) 

@JvmName("loginWithName")
fun login(user: User) {
}

fun login(passwd: String) {
}

// Kotlin 編譯正常
login(User("DHL"))
login("ByteCode")

// Java 編譯正常
MainKt.loginWithName("DHL");

所以無論是 Inline classes 還是 Value classes 如果沒有添加 @JvmName 注解却邓,都會(huì)存在這個(gè)問題。將生成的函數(shù)名稱打亂院水,是為了防止方法重載沖突申尤,或者 Java 意外調(diào)用。

Inline classes 是在 Kotlin 1.3 引入的 衙耕,在 Kotlin 1.5 時(shí)進(jìn)入了穩(wěn)定版本昧穿,廢棄了 inline 修飾符,引入了 Value classes橙喘。

什么是 Value classes

Inline classesValue classes 的子集时鸵, Value classesInline classes 會(huì)得到更多優(yōu)化,現(xiàn)階段 Value classesInline classes 一樣厅瞎,只能在構(gòu)造函數(shù)中傳入一個(gè)參數(shù)饰潜,參數(shù)需要用 val 聲明,將來可以在構(gòu)造函數(shù)中添加多個(gè)參數(shù)和簸,但是每個(gè)參數(shù)都需要用 val 聲明彭雾,官方說明如下圖所示。

將來如果支持添加多個(gè)參數(shù)锁保,那么它的使用范圍會(huì)越來越廣的薯酝。

升級(jí)到 Kotlin 1.5 之后,Inline classes 將被棄用爽柒,如下圖所示吴菠,編譯器將會(huì)給出警告。

根據(jù)提示目前唯一需要改變的是語法 inline 替換為 value, 然后在添加 @JvmInline 注解即可浩村。

@JvmInline
value class User(val name: String)

編譯后的效果和 Inline classes 是一樣的做葵,因此后面的案例將會(huì)使用 Value classes

Value classes 不能被繼承

因?yàn)?value class 編譯后將會(huì)添加 fianl 修飾符心墅,因此不能被繼承酿矢,同樣也不能繼承其他的類榨乎,如下圖所示。

Value classes 可以實(shí)現(xiàn)接口

雖然 value class 不能繼承其他的類瘫筐,但是可以實(shí)現(xiàn)接口蜜暑,代碼如下所示。

interface LoginInterface

@JvmInline
value class User(val name: String) : LoginInterface

fun testInline() {
    println(User("DHL"))
}


// 編譯后的代碼
public static final void testInline() {
  User var0 = User.box-impl(User.constructor-impl("DHL"));
  System.out.println(var0);
}

public static final User box_impl/* $FF was: box-impl*/(String v) {
  return new User(v);
}

正如代碼所示严肪,當(dāng) value class 實(shí)現(xiàn)接口時(shí)史煎,失去了內(nèi)聯(lián)效果谦屑,依然會(huì)在堆中創(chuàng)建 User 對(duì)象驳糯,除了實(shí)現(xiàn)接口的時(shí)候,沒有內(nèi)聯(lián)效果氢橙,當(dāng)對(duì)象為空的時(shí)候酝枢,也會(huì)失去了內(nèi)聯(lián)效果。

當(dāng)傳遞的對(duì)象為空的時(shí)候悍手,會(huì)失去內(nèi)聯(lián)效果

當(dāng)構(gòu)造函數(shù)的參數(shù)為基本數(shù)據(jù)類型帘睦,且傳遞的參數(shù) value class 的對(duì)象為空時(shí),將失去內(nèi)聯(lián)效果坦康,代碼如下所示竣付。

@JvmInline
value class User(val age: Int)

fun login(user: User?): Int = user?.age ?: 0

fun testInline() {
    println(login(User(10)))
}

// 編譯后的代碼
public static final int login_js0Jwf8/* $FF was: login-js0Jwf8*/(@Nullable User user) {
  return user != null ? user.unbox-impl() : 0;
}

public static final void testInline() {
  int var0 = login-js0Jwf8(User.box-impl(10));
  System.out.println(var0);
}

public static final User box_impl/* $FF was: box-impl*/(int v) {
  return new User(v);
}

正如你所見,依然會(huì)在堆中創(chuàng)建 User 對(duì)象滞欠,失去了內(nèi)聯(lián)效果古胆,這是因?yàn)?Int 不能直接轉(zhuǎn)換為 null

當(dāng)構(gòu)造函數(shù)的參數(shù)為 String筛璧,且傳遞的參數(shù) value class 的對(duì)象為空時(shí)逸绎,內(nèi)聯(lián)效果不會(huì)受到影響,代碼如下所示夭谤。

@JvmInline
value class User(val name: String)

fun login(user: User?): String = user?.name ?: ""

fun testInline() {
    println(login(User("DHL")))
}

// 編譯后的代碼
public static final String login_js0Jwf8/* $FF was: login-js0Jwf8*/(@Nullable String user) {
    // ......
  return var10000;
}

public static final void testInline() {
    String var0 = login-js0Jwf8("DHL");
    System.out.println(var0);
}

編譯后的 Java 代碼并沒有創(chuàng)建對(duì)象棺牧,傳遞給方法 login 的參數(shù) User 被替換為傳進(jìn)去的值 String

Value class 禁止使用 === 可以使用 ==

Kotlin 中的操作符 ===== 的區(qū)別朗儒,以及它們分別在什么場景下使用颊乘,更多信息可以前往查看 揭秘 Kotlin 中的 == 和 ===,這里簡單介紹一下醉锄。

Kotlin 提供了兩種方式用于對(duì)象的比較疲牵。

  • 比較對(duì)象的結(jié)構(gòu)是否相等( == 或者 equals

    Kotlin 中的操作符 == 等價(jià)于 equals 用于比較對(duì)象的結(jié)構(gòu)是否相等, 很多情況下使用的是 ==,因?yàn)閷?duì)于浮點(diǎn)類型 Float 和 Double榆鼠,其實(shí)現(xiàn)方法 equals 不遵循 IEEE 754 浮點(diǎn)運(yùn)算標(biāo)準(zhǔn)纲爸。

  • 比較對(duì)象的引用是否相等 ( === )

    Kotlin 中的操作符 === 用于比較對(duì)象的引用是否指向同一個(gè)對(duì)象,運(yùn)行時(shí)如果是基本數(shù)據(jù)類型 === 等價(jià)于 ==妆够。

其中 value class 比較特殊识啦,禁止使用操作符 ===负蚊,如下圖所示,編譯器將會(huì)給出警告颓哮。

因?yàn)椴僮鞣?=== 用于比較對(duì)象的引用是否相等家妆,因?yàn)閮?nèi)聯(lián)的原因,最終會(huì)替換為傳進(jìn)去的值冕茅。

但是操作符 == 是不受影響的伤极,操作符 == 用于比較對(duì)象的結(jié)構(gòu)是否相等,代碼如下所示姨伤。

fun testInline() {
    val u1 = User("DHL")
    val u2 = User("DHL")
    println(u1 == u2)
}

// 編譯后的代碼
public static final void testInline() {
  String u1 = "DHL";
  String u2 = "DHL";
  boolean var2 = User.equals-impl0(u1, u2);
  System.out.println(var2);
}

如果有幫助 點(diǎn)個(gè)贊 就是對(duì)我最大的鼓勵(lì)


最后推薦長期更新和維護(hù)的項(xiàng)目:

  • 個(gè)人博客哨坪,將所有文章進(jìn)行分類,歡迎前去查看 https://hi-dhl.com

  • KtKit 小巧而實(shí)用乍楚,用 Kotlin 語言編寫的工具庫当编,歡迎前去查看 KtKit

  • 計(jì)劃建立一個(gè)最全、最新的 AndroidX Jetpack 相關(guān)組件的實(shí)戰(zhàn)項(xiàng)目 以及 相關(guān)組件原理分析文章徒溪,正在逐漸增加 Jetpack 新成員忿偷,倉庫持續(xù)更新,歡迎前去查看 AndroidX-Jetpack-Practice

  • LeetCode / 劍指 offer / 國內(nèi)外大廠面試題 / 多線程 題解臊泌,語言 Java 和 kotlin鲤桥,包含多種解法、解題思路渠概、時(shí)間復(fù)雜度茶凳、空間復(fù)雜度分析

image

近期必讀熱門文章

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末剪芍,一起剝皮案震驚了整個(gè)濱河市塞淹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌罪裹,老刑警劉巖饱普,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異状共,居然都是意外死亡套耕,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門峡继,熙熙樓的掌柜王于貴愁眉苦臉地迎上來冯袍,“玉大人,你說我怎么就攤上這事】捣撸” “怎么了儡循?”我有些...
    開封第一講書人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長征冷。 經(jīng)常有香客問我择膝,道長,這世上最難降的妖魔是什么检激? 我笑而不...
    開封第一講書人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任肴捉,我火速辦了婚禮,結(jié)果婚禮上叔收,老公的妹妹穿的比我還像新娘齿穗。我一直安慰自己,他們只是感情好今穿,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開白布缤灵。 她就那樣靜靜地躺著伦籍,像睡著了一般蓝晒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上帖鸦,一...
    開封第一講書人閱讀 51,562評(píng)論 1 305
  • 那天芝薇,我揣著相機(jī)與錄音,去河邊找鬼作儿。 笑死洛二,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的攻锰。 我是一名探鬼主播晾嘶,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼娶吞!你這毒婦竟也來了垒迂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤妒蛇,失蹤者是張志新(化名)和其女友劉穎机断,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绣夺,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡吏奸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了陶耍。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奋蔚。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖烈钞,靈堂內(nèi)的尸體忽然破棺而出泊碑,到底是詐尸還是另有隱情产上,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布蛾狗,位于F島的核電站晋涣,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏沉桌。R本人自食惡果不足惜谢鹊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望留凭。 院中可真熱鬧佃扼,春花似錦、人聲如沸蔼夜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽求冷。三九已至瘤运,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間匠题,已是汗流浹背拯坟。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留韭山,地道東北人郁季。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像钱磅,于是被迫代替她去往敵國和親梦裂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容