内联类(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!