【kotlin 】内联类(value class / inline class)

官方文档:https://kotlinlang.org/docs/inline-classes.html

注:inline class 关键字已经被废弃,取而代之的是value class。现在使用内联类需要定义类为value class,并使用@JvmInline注解进行标注。

一、使用场景

有时候,根据业务需求,我们需要一些包装类。但是,包装类在运行时会造成一些不可避免的额外开支,比如堆上分配的额外空间。尤其是对于基本类型的包装类——因为基本类型在运行时会有很多其他优化,而包装类型没有。于是,内联类便应运而生了。

内联类在编码时作为一个其他类型的包装类使用,而在运行时会被拆开作为其内部值类型使用。

例如,当我们设计了一个动画:

class Animation(duration: Int) {
    // ...
}

duration 参数可能会让人迷惑:它的单位是什么?秒或者毫秒?(虽然注释可以解决一切问题,但它不在讨论范围之内)这个时候,就可以用到内联类。

我们可以创建一系列的内联类,来表示不同的时间单位:

@JvmInline
value class Millis(val value: Int) 
@JvmInline
value class Second(val value: Int)
// ...

假使 duration 参数单位是毫秒,那么将其类型修改为 Millis 类型即可:

class Animation(duration: Millis) {
    // ...
}

这样,当创建 Animation 对象的时候,就需要强制传入一个 Millis 类型的对象;如果传入的是一个 Second 类型的对象,编译器就会报错。

二、内联类允许的成员

内联类允许函数、init 块、以及没有 backing field 的属性。

@JvmInline
value class Name(val s: String) {
    init {
        require(s.length > 0) { }
    }

    val length: Int
        get() = s.length

    fun greet() {
        println("Hello, $s")
    }
}

fun main() {
    val name = Name("Kotlin")
    name.greet() // method `greet` is called as a static method
    println(name.length) // property getter is called as a static method
}

三、内联类和普通包装类的区别

对于原生类型来说,在运行时会进行大量的优化,而包装类不会进行处理。
而内联类在运行时,会自动使用内联类型而不是包装类型进行处理。

例如以下例子中:

// 毫秒
inline class Millisecond(val value: Long)

private fun doSomething(millisecond: Millisecond) {
    Log.i(TAG, "doSomething: $millisecond")
}

通过 IDE 的 Kotlin 字节码反编译功能,可以看到,生成的 Kotlin 字节码已经没有 Millisecond 类型了,而是直接使用的 Long 类型:
内联类反编译结果

而普通的包装类则不会进行如此优化:

class LongWrapper(val millis: Long)

private fun doSomething2(millisecond: LongWrapper) {
    Log.i(TAG, "doSomething: $millisecond")
}

包装类反编译结果

四、其他

1. 继承

内联类只允许继承接口,而不允许继承类,也不允许被其他类继承。

2. 与 typealias 的相比

在读取值的时候,value classtypealias 起到了类似的作用;但是,当进行赋值的时候,情况就变得不一样了。

假设我们现在使用一个类型 Name,表示一个字符串值。同时,有两个函数 setStringsetName

fun setName(name: Name) {}
fun setString(string: String) {}

在使用 typealias 的情况下,不管是 setString 还是 setName,均可以传入 Name 或是 String 类型的参数:

typealias Name = String
fun main() {
    val name: Name = "Bob"
    setName("Bob") // √
    setString(name) // √
}

但是,如果使用的是内联类,则二者均是不被允许的:

@JvmInline
value class Name(value: String)
fun main() {
    val name: Name = "Bob"
    setName("Bob") // ×
    setString(name) // ×
}

五、总结

  • 内联类相当于一个包装类,但是在编译时会自动进行拆包使用内部数据类型。
  • 在实际使用中,和普通包装类相同。
  • 内联类最大的优点在于对于基本类型的包装不会消耗额外的性能。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于LSTM的A股股票价格预测系统(torch) :从数据获取到模型训练的完整实现

1. 项目简介 本文介绍了一个使用LSTM(长短期记忆网络)进行股票价格预测的完整系统。该系统使用Python实现,集成了数据获取、预处理、模型训练和预测等功能。 这个代码使用的是 LSTM (Long Short-Term Memory) 模型,这是一种特殊的…

【python自动化五】接口自动化基础--requests的使用

python的接口请求可以用requests库,这个介绍就不多说了,网上说得很详细。 接下来直接记录下如何使用(当然也不限于自动化的使用) 1.安装requests requests也需要安装一下 pip install requests2.requests请求 1.常用的请求方法…

【NLP 5、深度学习的基本原理】

目录 一、梯度下降算法 1.引例 —— 找极小值问题 目标: 方法: 2.梯度 例: 3.求解目标 为什么损失函数越小越好 4.梯度下降法 代码实现 5.细节问题 6.梯度爆炸和梯度消失 梯度爆炸 梯度消失 7.过拟合和欠拟合 欠拟合(Underfitting…

DAY168内网对抗-基石框架篇单域架构域内应用控制成员组成用户策略信息收集环境搭建

知识点: 1、基石框架篇-单域架构-权限控制-用户和网络 2、基石框架篇-单域架构-环境搭建-准备和加入 3、基石框架篇-单域架构-信息收集-手工和工具 1、工作组(局域网) 将不同的计算机按照功能分别列入不同的工作组。想要访问某个部门的资源,只要在“…

MATLAB 建筑顶面面积计算(95)

MATLAB 建筑顶面面积计算(95) 一、算法介绍二、算法实现1.代码2.结果一、算法介绍 根据给出的建筑顶面点云,计算建筑面积,具体的方法实现和结果如下: 二、算法实现 1.代码 代码如下(示例): % 从 PLY 文件读取点云数据 filename = D:\shuju\屋顶2.ply; % 替换为你的…

Mac M1 安装数据库

1. Docker下载 由于Sqlserver和达梦等数据库,不支持M系列的芯片,所以我们通过docker安装 下载并安装docker: https://www.docker.com/get-started/ 安装完成后,打开docker 2. SQL Server 安装 2.1 安装 打开终端,执行命令 doc…

二十(GIT3)、echarts(折线图、柱状图、饼图)、黑马就业数据平台(主页图表实现、闭包了解、学生信息渲染)

1. echarts 数据可视化:将数据转换为图形,数据特点更加突出 echarts:一个基于 JavaScript 的开源可视化图表库 echarts官网 1.1 echarts核心使用步骤 // 1. 基于准备好的dom,初始化echarts实例 const myChart echarts.init…

软考高级架构-9.4.4-双机热备技术 与 服务器集群技术

一、双机热备 1、特点: 软硬件结合:系统由两台服务器(主机和备机)、一个共享存储(通常为磁盘阵列柜)、以及双机热备软件(提供心跳检测、故障转移和资源管理功能的核心软件)组成。 …

【Java若依框架】RuoYi-Vue的前端和后端配置步骤和启动步骤

🎙告诉你:Java是世界上最美好的语言 💎比较擅长的领域:前端开发 是的,我需要您的: 🧡点赞❤️关注💙收藏💛 是我持续下去的动力! 目录 一. 作者有话说 …

Kubernetes Nginx-Ingress | 禁用HSTS/禁止重定向到https

目录 前言禁用HSTS禁止重定向到https关闭 HSTS 和设置 ssl-redirect 为 false 的区别 前言 客户请求经过ingress到服务后,默认加上了strict-transport-security,导致客户服务跨域请求失败,具体Response Headers信息如下; 分析 n…

小程序入门学习(八)之页面事件

一、下拉刷新新事件 1. 什么是下拉刷新 下拉刷新是移动端的专有名词,指的是通过手指在屏幕上的下拉滑动操作,从而重新加载页面数据的行为。 2. 启用下拉刷新 启用下拉刷新有两种方式: 全局开启下拉刷新:在 app.json 的 window…

C++(十二)

前言: 本文将进一步讲解C中,条件判断语句以及它是如何运行的以及内部逻辑。 一,if-else,if-else语句。 在if语句中,只能判断两个条件的变量,若想实现判断两个以上条件的变体,就需要使用if-else,if-else语…

[Linux]文件属性和权限

目录 一.Linux文件的属性二.Linux用户权限分类三.文件权限的查询与修改1.修改用户的权限1).一般法2).8进制法 2.修改所属组和所属者3.如何在创建文件时权限预分配 在学习linux的时候,我们用ll命令显示文件的详情信息,难免会发现文件名前面会有一大堆其它…

ElK 8 收集 MySQL 慢查询日志并通过 ElastAlert2 告警至飞书

文章目录 1. 说明2. 启个 mysql3. 设置慢查询4. filebeat 设置5. 触发慢查询6. MySQL 告警至飞书 1. 说明 elk 版本:8.15.0 2. 启个 mysql docker-compose.yml 中 mysql: mysql:# restart: alwaysimage: mysql:8.0.27# ports:# - "3306:3306&q…

springSecurity权限控制

权限控制:不同的用户可以使用不同的功能。 我们不能在前端判断用户权限来控制显示哪些按钮,因为这样,有人会获取该功能对应的接口,就不需要通过前端,直接发送请求实现功能了。所以需要在后端进行权限判断。&#xff0…

力扣打卡9:重排链表

链接:143. 重排链表 - 力扣(LeetCode) 这是一道操作链表的题。按照要求,我们可以将解题的步骤分成三步。 1.找链表中间结点(我使用了快慢指针寻找),并断开。 2.现在有2链表,将后段…

计算机键盘的演变 | 键盘键名称及其功能 | 键盘指法

注:本篇为 “键盘的演变及其功能” 相关几篇文章合辑。 英文部分机翻未校。 The Evolution of Keyboards: From Typewriters to Tech Marvels 键盘的演变:从打字机到技术奇迹 Introduction 介绍 The keyboard has journeyed from a humble mechanical…

【Appium报错】安装uiautomator2失败

目录 1、通过nmp安装uiautomator2:失败 2、通过 Appium 的平台直接安装驱动程序 3、通过pip 来安装 uiautomator2 1、通过nmp安装uiautomator2:失败 我先是通过npm安装的uiautomator2,也显示已经安装成功了: npm install -g …

SSM整合原理实战案例《任务列表案例》

一、前端程序搭建和运行: 1.整合案例介绍和接口分析: (1).案例功能预览: (2).接口分析: 学习计划分页查询 /* 需求说明查询全部数据页数据 请求urischedule/{pageSize}/{currentPage} 请求方式 get 响应的json{"code":200,"flag":true,"data&…

Chrome扩展程序开发示例

项目文件夹内文件如下: manifest.json文件内容: {"manifest_version": 3,"name": "我的法宝","description": "我的有魔法的宝贝","version": "1.0","icons": {"…