上海网站建设费用多少,windows 2003 取消网站访问密码,wordpress问答插件中文免费版,wifi域名是什么有时#xff0c;将一个值包装在一个类中可以创建一个更具领域特定类型的类。然而#xff0c;由于额外的堆分配#xff0c;这会引入运行时开销。此外#xff0c;如果包装的类型是原始类型#xff0c;性能损失是显著的#xff0c;因为原始类型通常由运行时进行了大量优化将一个值包装在一个类中可以创建一个更具领域特定类型的类。然而由于额外的堆分配这会引入运行时开销。此外如果包装的类型是原始类型性能损失是显著的因为原始类型通常由运行时进行了大量优化而它们的包装类没有得到任何特殊处理。
为了解决这些问题kotlin引入了一种特殊类型的类称为内联类inline class。内联类是值类value-based classes的一个子集。它们没有身份只能持有值。
要声明一个内联类请在类名之前使用value修饰符
value class Password(private val s: String)要在JVM后端声明一个内联类可以在类声明之前使用value修饰符以及JvmInline注解
// For JVM backends
JvmInline
value class Password(private val s: String)内联类的主构造函数必须具有一个初始化的单个属性。在运行时内联类的实例将使用该单个属性来表示
// 实际上不会对Password类进行实例化
// 在运行时securePassword只包含String
val securePassword Password(Dont try this in production)这就是内联类的主要特性它启发了名称inline类的数据被内联到其使用的地方类似于内联函数的内容被内联到调用站点
成员
内联类支持常规类的某些功能。特别是它们可以声明属性和函数具有初始化块和辅助构造函数
JvmInline
value class Person(private val fullName: String) {init {require(fullName.isNotEmpty()) {Full name shouldnt be empty}}constructor(firstName: String, lastName: String) : this($firstName $lastName) {require(lastName.isNotBlank()) {Last name shouldnt be empty}}val length: Intget() fullName.lengthfun greet() {println(Hello, $fullName)}
}fun main() {val name1 Person(Kotlin, Mascot)val name2 Person(Kodee)name1.greet() // the greet() function is called as a static methodprintln(name2.length) // property getter is called as a static method
}内联类的属性不能有backing fields。它们只能具有简单的可计算属性不支持lateinit或委托属性 继承
内联类可以实现接口
interface Printable {fun prettyPrint(): String
}JvmInline
value class Name(val s: String) : Printable {override fun prettyPrint(): String Lets $s!
}fun main() {val name Name(Kotlin)println(name.prettyPrint())
}内联类不能继承其它类也不能被其它类继承
表示方式Representation
在生成的代码中kotlin编译器会为每个内联类保留一个包装器。在运行时内联类实例可以表示为包装器或基础类型。这类似于Int可以表示为原始的int类型或包装器Integer
编译器倾向于使用基础类型而不是包装器来生成性能最佳和经过优化的代码。然而有时候需要保留包装器。一般而言当内联类用作另一种类型时它们会被装箱boxed
interface IJvmInline
value class Foo(val i: Int) : Ifun asInline(f: Foo) {}
fun T asGeneric(x: T) {}
fun asInterface(i: I) {}
fun asNullable(i: Foo?) {}fun T id(x: T): T xfun main() {val f Foo(42)asInline(f) // 不装箱: 使用Foo自己asGeneric(f) // 装箱: 使用泛型 TasInterface(f) // 装箱: 使用 IasNullable(f) // 装箱:使用 Foo?, 这和Foo不同// 调用id()方法时。首先装箱成T类型返回时拆箱成Foo// 最后c是未装箱的状态和f一样val c id(f)
}由于内联类可以同时表示为基础值和包装器因此引用相等性是无意义的是被禁止的。
内联类还可以具有泛型类型参数作为基础类型。在这种情况下编译器将其映射为Any?或为类型参数的上界。
JvmInline
value class UserIdT(val value: T)fun compute(s: UserIdString) {} // compiler generates fun compute-hashcode(s: Any?)编译器会优化 compute 函数的签名将其转换为 fun compute-hashcode(s: Any?)其中 hashcode 是一个哈希码用于确保函数名称的唯一性。这意味着编译器将 UserIdString 类型的参数转换为 Any? 类型的参数。 名称修饰Mangling
由于内联类被编译为其基础类型这可能导致各种晦涩的错误例如意外的平台签名冲突
JvmInline
value 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代码调用
接受内联类参数的函数时可以手动禁用名称修饰。为此需要在函数声明之前添加JvmName注解
JvmInline
value class UInt(val x: Int)fun compute(x: Int) { }JvmName(computeUInt)
fun compute(x: UInt) { }内联类VS类行别名
两者似乎都引入了一个新的类型并且在运行时都会表示为其基础类型
然而关键的区别在于类型别名可以与其基础类型以及具有相同基础类型的其他类型别名进行赋值兼容而内联类则不行。
换句话说内联类引入了一个真正的新类型而类型别名只是为现有类型引入了一个替代名称别名
typealias NameTypeAlias StringJvmInline
value 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(string) // 传递基础类型而不是别名acceptNameInlineClass(string) // 报错 -- 参数类型不匹配
}内联类和委托 委托后边讲解 使用内联类的内联值进行委托实现是允许的可以通过接口实现
interface MyInterface {fun bar()fun foo() foo
}JvmInline
value class MyInterfaceWrapper(val myInterface: MyInterface) : MyInterface by myInterfacefun main() {val my MyInterfaceWrapper(object : MyInterface {override fun bar() {// body}})println(my.foo()) // prints foo
}MyInterfaceWrapper 类型实例可以使用委托的 MyInterface 实现的方法包括默认实现的 foo() 方法。在这种方式下通过委托实现可以将内联类的功能扩展到具体的接口实现上