內聯類(Inline Class)
內聯類的支持版本目前是1.3及以上。函數
內聯類的使用其實很簡單,只須要咱們在class前面加上一個inline關鍵字便可,以下面這樣:性能
inline class WoMan(private val name: String) { val length: Int get() =name.length fun greet() { println("Hello, $name") } }
跟普通的類看上去沒什麼區別,不過咱們show bytecode以後看看代碼是怎麼樣的this
public static final int getLength_impl/* $FF was: getLength-impl*/(String $this) { return $this.length(); }
public static final void greet_impl/* $FF was: greet-impl*/(String $this) { String var1 = "Hello, " + $this; boolean var2 = false; System.out.println(var1); }
String name = WoMan.constructor-impl("唐三"); WoMan.greet-impl(name);
其實最後都變成了靜態方法調用了,那若是咱們不用inline關鍵字修飾的話會是怎麼樣呢spa
public final class WoMan { private final String name; public final int getLength() { return this.name.length(); } public final void greet() { String var1 = "Hello, " + this.name; boolean var2 = false; System.out.println(var1); } public WoMan(@NotNull String name) { Intrinsics.checkNotNullParameter(name, "name"); super(); this.name = name; } }
其實就是簡單的Java類而已,調用的話咱們仍是得對象方法才能夠。設計
WoMan name = new WoMan("唐三"); name.greet();
而後咱們再說一下內聯函數的使用要求:內聯類必須含有惟⼀的⼀個屬性在主構造函數中初始化。在運⾏時,將使⽤這個惟⼀屬性來表⽰內聯類的實例。code
其實就是起到了包裝器的做用,咱們發現使用的時候跟普通的類的使用方法同樣,沒有任何區別,可是咱們在性能上就已經作得很好了,咱們在實例化一個對象的時候,就會把對象存儲到JVM堆上,咱們在存儲和使用對象實例時會有性能損失 - 堆分配和內存提取的性能代價很高。雖然看起來每一個對象性能開銷微不足道,可是累積起來,它對代碼運行速度產生嚴重的影響。若是咱們可以在不受性能影響的狀況下得到強類型系統的全部好處,那不是很好嗎?實際上,Kotlin新特性inline class
就是爲了解決這樣的問題而設計的。對象
這樣咱們即在性能方面作了節省,還不影響咱們得到強類型的使用的全部的好處,這樣何樂不爲呢?繼承
上面的例子當中咱們聲明的內聯類對象的時候其實就至關於聲明瞭咱們內聯類裏面包裝的變量而已,從而避免了對象的建立和銷燬。接口
內聯類的限制
內聯類的柱構造函數裏面只能有一個參數而且是隻能是可讀的,可是呢,它的內部是能夠擁有成員屬性的,只要它們僅基於構造器中那個基礎值計算,或者從能夠靜態解析的某個值或對象計算 - 來自單例,頂級對象,常量等。能夠將參數設置爲私有的,可是構造函數必須是共有的,咱們的內聯類能夠繼承接口,可是不能繼承類,也不能被任何類繼承,不能做爲內部類實現,也不支持內聯枚舉類。內存
類型別名
typealias類型別名的時候跟內聯類有一些類似之處,也容易混淆。
由於它們都包含基礎類型,因此內聯類很容易與類型別名混淆。可是有一些關鍵的差別使它們在不一樣的場景下得以應用。
好比咱們須要定義一個變量,多是用戶名稱或者用戶密碼等,那麼類型確定是字符串嘛,做爲函數形參的聲明的時候咱們通常都是name:String便可,但是咱們想表達的更清楚一些呢,就能夠用類型別名還使用咱們本身定義的名字。
typealias userName = String
class WoMan(private val name: userName) {}
能夠看到使用起來跟String沒什麼區別,只是表達的更清楚了。
內聯函數一方面來講使用的時候咱們須要進行拆箱的操做,而類型別名就不須要了,但是類型別名在同一類型的時候是沒法區分,好比咱們同事聲明瞭兩個類型別名username,password並且都是字符串,但是咱們的定義函數方法裏面須要的參數是能夠改變順序的,由於咱們的參數類型雖然是不一樣的類型別名,可是都是一個String類型的,因此這一點內聯函數是徹底能夠不用考慮的,由於內聯函數就是一個基本類型的包裝體,不一樣的包裝體確定不同,因此咱們的順序是不能改變的。
typealias userName = String typealias passWord = String fun test(name: userName, pwd: passWord) { }
上面就很容易看到了,當咱們name和pwd的傳參數順序不同的時候,編譯器是沒法作出檢測的,運行期也不會報錯的。
可是內聯函數就是包裝以後就是不一樣的類。
inline class Username(val name: String) inline class Password(val pwd: String) fun test2(name: Username, pwd: Password) { }
當咱們調用函數的時候傳遞的參數順序改變的話,編譯器是會報錯的。
因此使用起來各取所需吧,各有各的使用場景。
ok!