23:kotlin类和对象 -- 内联值类(Inline value classes)

有时,将一个值包装在一个类中可以创建一个更具领域特定类型的类。然而,由于额外的堆分配,这会引入运行时开销。此外,如果包装的类型是原始类型,性能损失是显著的,因为原始类型通常由运行时进行了大量优化,而它们的包装类没有得到任何特殊处理。

为了解决这些问题,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("Don't try this in production")

这就是内联类的主要特性,它启发了名称"inline":类的数据被内联到其使用的地方(类似于内联函数的内容被内联到调用站点)

成员

内联类支持常规类的某些功能。特别是,它们可以声明属性和函数,具有初始化块和辅助构造函数

@JvmInline
value class Person(private val fullName: String) {init {require(fullName.isNotEmpty()) {"Full name shouldn't be empty"}}constructor(firstName: String, lastName: String) : this("$firstName $lastName") {require(lastName.isNotBlank()) {"Last name shouldn't 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 = "Let's $s!"
}fun main() {val name = Name("Kotlin")println(name.prettyPrint())
}

内联类不能继承其它类,也不能被其它类继承

表示方式(Representation)

在生成的代码中,kotlin编译器会为每个内联类保留一个包装器。在运行时,内联类实例可以表示为包装器或基础类型。这类似于Int可以表示为原始的int类型或包装器Integer

编译器倾向于使用基础类型而不是包装器来生成性能最佳和经过优化的代码。然而,有时候需要保留包装器。一般而言,当内联类用作另一种类型时,它们会被装箱(boxed)

interface I@JvmInline
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 UserId<T>(val value: T)fun compute(s: UserId<String>) {} // compiler generates fun compute-<hashcode>(s: Any?)

编译器会优化 compute 函数的签名,将其转换为 fun compute-<hashcode>(s: Any?),其中 <hashcode> 是一个哈希码,用于确保函数名称的唯一性。这意味着编译器将 UserId<String> 类型的参数转换为 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 = String@JvmInline
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() 方法。在这种方式下,通过委托实现可以将内联类的功能扩展到具体的接口实现上

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/202141.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【c】杨辉三角

下面介绍两种方法 1.利用上面性质的第五条&#xff0c;我们可以求各行各列的组合数 2.利用上面性质的第7条&#xff0c;我们可以用数组完成 下面附上代码 1. #include<stdio.h> void fact(int n ,int m )//求组合数 {long long int sum11;long long int sum21;int a…

面试被问到 HTTP和HTTPS的区别有哪些?你该如何回答~

HTTP和HTTPS的区别有哪些&#xff0c;主要从以下几个方面来说&#xff1a; 1.安全性 HTTP和HTTPS是两种不同的协议&#xff0c;它们之间最主要的区别在于安全性。HTTP协议以明文方式发送内容&#xff0c;不提供任何方式的数据加密&#xff0c;容易被攻击者截取信息。 HTTPS则在…

ICO 图标

ICO是一种图标文件格式&#xff0c;图标文件可以存储单个图案、多尺寸、多色板的图标文件。一个图标实际上是多张不同格式的图片的集合体&#xff0c;并且还包含了一定的透明区域。 它是图标文件格式的一种&#xff0c;可以存储单个图案、多尺寸、多色板的图标文件。 图标是具…

深入解析Socks5代理技术:保障隐私与网络安全的利器

Socks5代理&#xff0c;作为一种网络通信协议&#xff0c;广泛应用于网络安全、隐私保护以及访问控制等领域。本文将深入探讨Socks5代理的技术原理、特性以及在实际应用中的重要性。 1. Socks5代理的基本原理 Socks5代理是一种开放式协议&#xff0c;主要用于客户端与服务器之…

LTD254次升级 | 订单批打印 • 官网搭“抖音“ • 合伙人添权益

1、 商城订单支持打印功能&#xff1b; 2、 H5/小程序商城新增一款首页样式&#xff1b; 3、 社区中视频支持抖音方式浏览&#xff1b; 4、 极速官微优化管理页面布局、优化海报分享样式&#xff1b; 5、 新增一款轮播模块&#xff1b; 6、 已知问题修复与优化&#xff1b; 01 …

<sa8650>Safety Monitor 之 API介绍 (第二部分)

&#xff1c;sa8650&#xff1e;Safety Monitor 之 API介绍 4.由APSS安全监视器支持的接口4.1数据结构文件4.1.1 struct sm_handle4.1.2 struct safety_msg_initial_fault4.1.3 struct safety_msg_notify_fault 4.2 Enumeration documentation4.2.1 safety_fault_subsystem4.2…

前端:让一个div悬浮在另一个div之上

使用 CSS 的 position 属性和 z-index 属性 首先&#xff0c;将第二个 div 元素的 position 属性设为 relative 或 absolute。这样可以让该元素成为一个定位元素&#xff0c;使得后代元素可以相对于它进行定位。 然后&#xff0c;将要悬浮的 div 元素的 position 属性设为 ab…

分布式锁常见实现方案

分布式锁常见实现方案 基于 Redis 实现分布式锁 如何基于 Redis 实现一个最简易的分布式锁&#xff1f; 不论是本地锁还是分布式锁&#xff0c;核心都在于“互斥”。 在 Redis 中&#xff0c; SETNX 命令是可以帮助我们实现互斥。SETNX 即 SET if Not eXists (对应 Java 中…

Element-UI 动态控制输入组件类型,定义代码组件、前端模板

文章目录 前言组件模板的定义定义组件信息定义控制其他组件类型的组件定义被控制组件的类型以及信息前端模板的定义效果图前端模板的使用总结前言 在做复杂的动态表单,实现业务动态变动,比如有一条需要动态添加的el-form-item中包含了多个输入框,并实现表单验证,但在eleme…

Redis7--基础篇7(哨兵sentinel)

1. 关于哨兵的介绍 1、监控redis运行状态&#xff0c;包括master和slave&#xff08;主从监控&#xff09; 2、哨兵可以将故障转移的结果发送给客户端&#xff08;消息通知&#xff09; 3、当master down机&#xff0c;能自动将slave切换成新master&#xff08;故障转移&#…

软件测试理论

含义&#xff1a;使用技术手段来验证软件是否满足使用需求 目的&#xff1a;减少软件缺陷&#xff0c;保障软件质量 单元测试&#xff1a;对模块/函数进行的测试 集成测试&#xff1a;把多个模块/函数组装到一起进行的测试 系统测试&#xff1a;计算机程序结合外设网络等其…

springboot077基于SpringBoot的汽车票网上预订系统

springboot077基于SpringBoot的汽车票网上预订系统 成品项目已经更新&#xff01;同学们可以打开链接查看&#xff01;需要定做的及时联系我&#xff01;专业团队定做&#xff01;全程包售后&#xff01; 2000套项目视频链接&#xff1a;https://pan.baidu.com/s/1N4L3zMQ9n…

流量异常-挂马造成百度收录异常关键词之解决方案(虚拟主机)

一.异常现象&#xff1a;流量突然暴涨&#xff0c;达到平时流量几倍乃至几十倍&#xff0c;大多数情况下因流量超标网站被停止。 二.排查原因&#xff1a; 1.首先分析web日志&#xff1a;访问量明显的成倍、几十倍的增加&#xff1b;访问页面不同&#xff1b;访问IP分散并不固…

err_connect_length_mismatch错误

原因: 官网解释为&#xff1a;err_content_length_mismatch:错误的内容长度不匹配&#xff08;请求的Heather 里content-length长度与返回的content-length不一致&#xff09; 问题截图: 分析: 由截图可见,静态资源加载错误,提示err_content_length_mismatch,经排查,网络页签…

“新KG”视点 | 知识与大模型融合技术在电信领域应用探索

OpenKG 大模型专辑 导读 知识图谱和大型语言模型都是用来表示和处理知识的手段。大模型补足了理解语言的能力&#xff0c;知识图谱则丰富了表示知识的方式&#xff0c;两者的深度结合必将为人工智能提供更为全面、可靠、可控的知识处理方法。在这一背景下&#xff0c;OpenKG组织…

M2芯片回顾

M芯片&#xff0c; 一竟到底&#xff1a; M1芯片的体积&#xff1a; M2 代表 M 系列芯片的第二代&#xff1a; 其进一步提升了芯片的性能和功率 &#xff0c;这也是 M 芯片目前的追求&#xff1a;最大化性能的同时&#xff0c;最大限度降低功耗。 UMA 统一内存架构被再一次提到…

前端监控文件上传进度

前端&#xff1a; <!doctype html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>Document</title></…

什么是 shell 脚本?

一、什么是 shell&#xff1f; Shell 是一个应用程序&#xff0c;它负责接收用户输入的命令&#xff0c;然后根据命令做出相应的动作&#xff0c; Shell 负责将应用层或者用户输入的命令传递给系统内核&#xff0c;由操作系统内核来完成相应的工作&#xff0c;然后将结果反馈给…

西门子 ULTRAMAT 23多组分气体分析仪 端子接线说明

后面板端子分布图 RS485接口 模拟量&#xff08;开关量&#xff09;接口 特此记录 anlog 2023年12月6日

天眼销为电销行业降低获客成本

当下&#xff0c;做电销的老板都有一个深刻体会&#xff1a;市场竞争越来越激烈&#xff0c;获客成本不断攀升&#xff0c;但效率不升返降&#xff0c;企业经营困难。特别是在这一两年&#xff0c;市场环境紧张&#xff0c;业务不好开展&#xff0c;更是雪上加霜。 销售也感觉…