24:kotlin 类和对象 -- 对象表达式和声明(Object expressions and declarations)

如果需要创建一个稍微修改了某个类的对象,而不需要显式地声明一个新的子类。Kotlin可以通过对象表达式(object expressions)和对象声明(object declarations)来处理这种情况。

对象表达式

对象表达式用于创建匿名类的对象。这种类对于一次性使用非常有用。你可以从头开始定义它们,继承现有的类,或者实现接口。匿名类的实例也被称为匿名对象,因为它们是由表达式定义的,而不是由名称定义的。

从头开始创建匿名对象

对象表达式以关键字object开始。

定义一个普通的无父类的匿名对象

val helloWorld = object {val hello = "Hello"val world = "World"// 重写Any的toString方法override fun toString() = "$hello $world"
}fun main() {print(helloWorld)
}

继承

继承MouseAdapter

window.addMouseListener(object : MouseAdapter() {override fun mouseClicked(e: MouseEvent) { /*...*/ }override fun mouseEntered(e: MouseEvent) { /*...*/ }
})

如果超类型有构造函数,则向其传递适当的构造函数参数

open class A(x: Int) {public open val y: Int = x
}interface B { /*...*/ }val ab: A = object : A(1), B {override val y = 15
}

将匿名对象用作返回类型和值类型

当匿名对象用作本地或私有(函数或属性)的类型时(非内联声明),通过该函数或属性可以访问其所有成员

class C {private fun getObject() = object {val x: String = "x"}fun printX() {println(getObject().x)}
}

如果获取匿名类的函数或属性是公共的或私有内联的,则其实际类型为:

  • 如果匿名对象没有声明的超类型,则为 Any
  • 如果有确切的一个声明的超类型,则为匿名对象的声明的超类型
  • 如果有多于一个声明的超类型,则为显式声明的类型

在所有这些情况下,无法访问在匿名对象中添加的成员。如果在函数或属性的实际类型中声明了重写的成员,则可以访问这些成员

interface A {fun funFromA() {}
}
interface Bclass C {// 返回类型是Any,x变量 不能访问fun getObject() = object {val x: String = "x"}// 返回类型是A,x变量 不能访问fun getObjectA() = object: A {override fun funFromA() {}val x: String = "x"}// 返回类型是B,funFromA()和 x变量 均不能访问fun getObjectB(): B = object: A, B { // 需要显式返回类型override fun funFromA() {}val x: String = "x"}
}

访问匿名对象中的变量

对象表达式中的代码可以访问来自封闭范围的变量

fun countClicks(window: JComponent) {var clickCount = 0var enterCount = 0window.addMouseListener(object : MouseAdapter() {override fun mouseClicked(e: MouseEvent) {clickCount++}override fun mouseEntered(e: MouseEvent) {enterCount++}})
}

对象声明

声明单例模式

object DataProviderManager {fun registerDataProvider(provider: DataProvider) {// ...}val allDataProviders: Collection<DataProvider> = MutableList(0){DataProvider()}get() = field// ...
}

对象声明是在 object 关键字后面跟着一个名称。就像变量声明一样,对象声明不是一个表达式,不能在赋值语句的右侧使用

对象声明在首次访问时进行初始化,是线程安全的

要引用对象,直接使用它的名称

DataProviderManager.registerDataProvider(...)

继承

object DefaultListener : MouseAdapter() {override fun mouseClicked(e: MouseEvent) { ... }override fun mouseEntered(e: MouseEvent) { ... }
}

对象声明不能在方法内部(不能是局部对象)

数据对象

打印一个普通的对象声明时,其字符串表示包含了它的名称和对象的哈希值

object MyObjectfun main() {println(MyObject()) // MyObject@1f32e575
}

就像数据类一样,你可以用 data修饰符标记一个对象声明。这会告诉编译器为你的对象生成一些函数

  • toString() 返回数据对象的名称
  • equals() hashCode() 成对出现

对于数据对象,不能提供自定义的 equalshashCode 实现

数据对象的 toString() 函数返回对象的名称

data object MyDataObject {val x: Int = 3
}fun main() {println(MyDataObject) // MyDataObject
}

equals() 函数用于数据对象,确保具有与你的数据对象相同类型的所有对象都被视为相等。在大多数情况下,你在运行时只会有一个数据对象的实例(毕竟,数据对象声明了一个单例)。然而,在特殊情况下,如果在运行时生成了另一个相同类型的对象(例如,通过使用平台反射与java.lang.reflect或使用此 API 底层的 JVM 序列化库),这确保这些对象被视为相等

确保你只通过结构比较数据对象(使用 == 操作符),而绝不是通过引用比较(使用 === 操作符)。这有助于在运行时存在多个数据对象实例时避免陷阱

data object MySingletonfun main() {val evilTwin = createInstanceViaReflection()println(MySingleton) // MySingletonprintln(evilTwin) // MySingleton// 使用 == 比较两个数据类返回 trueprintln(MySingleton == evilTwin) // true// 不要通过 === 比较两个数据类println(MySingleton === evilTwin) // false
}fun createInstanceViaReflection(): MySingleton {// kotlin反射不允许创建数据对象// 当前方法会强制创建一个MySingleton对象// 不要在代码中这样做return (MySingleton.javaClass.declaredConstructors[0].apply { isAccessible = true } as Constructor<MySingleton>).newInstance()
}

生成的 hashCode() 函数与 equals() 一样 ,因此数据对象的所有运行时实例都具有相同的哈希码

数据对象和数据类之间的差异
虽然数据对象和数据类声明通常一起使用并具有一些相似之处,但有一些函数不会为数据对象生成

  • 没有copy()函数。因为数据对象声明旨在用作单例对象,所以不会生成 copy()函数。单例模式将类的实例化限制为单个实例,允许创建实例的副本将违反这一原则
  • 没有 componentN() 函数。与数据类不同,数据对象没有任何数据属性。由于在没有数据属性的情况下尝试解构这样的对象是没有意义的,因此不会生成 componentN() 函数

使用数据对象处理密封层次结构

数据对象声明在处理密封层次结构(如密封类或密封接口)时特别有用,这可以更简洁的定义一个与其他密封类相同的类型

sealed interface ReadResult
data class Number(val number: Int) : ReadResult
data class Text(val text: String) : ReadResult
data object EndOfFile : ReadResultfun printReadResult(r: ReadResult) {when(r) {is Number -> println("Num(${r.number}")is Text -> println("Txt(${r.text}")is EndOfFile -> println("EOF")}
}fun main() {printReadResult(EndOfFile) // EOF
}

伴生对象

在类内部,可以使用 companion关键字标记的对象声明

class MyClass {companion object Factory {fun create(): MyClass = MyClass()}
}

伴生对象的成员可以通过使用类名作为限定符而被简单调用

val instance = MyClass.create()

伴生对象的名称可以省略,默认名称Companion

class MyClass {companion object{ }
}val x = MyClass.Companion
fun main() {println(x)	// io.example.MyClass$default@35fb3008
}

类成员可以访问相应伴生对象的私有成员

一个类的名称(单独使用,而不是作为另一个名称的限定符)充当对该类的伴生对象的引用(无论是否命名)

class MyClass {companion object{ }
}val y = MyClass	// 外部类名称做伴生对象的引用
fun main() {println(y)	// io.example.MyClass$default@35fb3008
}

请注意,尽管伴生对象的成员看起来像是其他语言中的静态成员,但在运行时,它们仍然是真实对象的实例成员,因此可以实现接口

interface Factory<T> {fun create(): T
}class MyClass {companion object : Factory<MyClass> {override fun create(): MyClass = MyClass()}
}fun main() {val f: Factory<MyClass> = MyClassprintln(f.create()) // io.example.MyClass@7225790e
}

JVM上,如果使用 @JvmStatic 注解,可以将伴生对象的成员生成为真正的静态方法和字段。有关详细信息,查看这里。

对象表达式和对象声明之间存在一个重要的语义差异:

  • 对象表达式是立即执行(和初始化)的,它们在使用的地方立即执行。
  • 对象声明是懒初始化的,当首次访问时才会初始化。
  • 伴生对象在相应的类被加载(解析)时初始化,这符合 Java静态初始化程序的语义

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

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

相关文章

中国移动频段划分

1、900MHz&#xff08;Band8&#xff09;上行&#xff1a;889-904MHz&#xff0c;下行&#xff1a;934-949MHz&#xff0c;带宽共计15MHz&#xff0c;目前部署&#xff1a;2G/NB-IoT/4G 2、1800MHz&#xff08;Band3&#xff09;上行&#xff1a;1710-1735MHz&#xff0c;下行…

kyuubi整合flink yarn application model

目录 概述配置flink 配置kyuubi 配置kyuubi-defaults.confkyuubi-env.shhive 验证启动kyuubibeeline 连接使用hive catalogsql测试 结束 概述 flink 版本 1.17.1、kyuubi 1.8.0、hive 3.1.3、paimon 0.5 整合过程中&#xff0c;需要注意对应的版本。 注意以上版本 姊妹篇 k…

机器学习之蛙跳算法(Jumping Frog Optimization,JFO)

概念 蛙跳算法(Jumping Frog Optimization,JFO)是一种基于仿生学和自然界觅食行为的启发式优化算法。该算法模拟了青蛙在寻找食物时的跳跃行为。青蛙通过一系列跳跃来寻找最优位置,而这些跳跃的长度和方向是通过计算当前位置的适应度值来确定的。 蛙跳算法的基本步骤: …

AXURE地图获取方法

AXURE地图截取地址 https://axhub.im/maps/ 1、点击上方地图或筛选所需地区的地图&#xff0c;点击复制到 Axure 按钮&#xff0c;到 Axure 粘贴就可以了 2、复制到 Axure 后&#xff0c;转化为 svg 图形&#xff0c;就可以随意更改尺寸/颜色/边框&#xff0c;具体操作如下&am…

微型5G网关如何满足智能巡检机器人应用

在规模庞大、设施复杂的炼化厂、钢铁厂、工业园区等大型、巨型区域&#xff0c;时刻需要对各类设施设备巡查监测&#xff0c;保障生产运行安全可控。传统的人工巡检存在着心态松懈、工作低效、工作强度高、工作环境恶劣等问题&#xff0c;仍然存在安全隐患。 而随着物联网、5G、…

【Flink on k8s】- 6 - Flink 核心特性

目录 1、系统架构 1.1 Flink api 抽象 1.2 数据流图 1.1.1、流式计算引擎逻辑视角与物理视角

稀疏矩阵的操作(数据结构实训)

题目&#xff1a; 标准输入输出 题目描述&#xff1a; 稀疏矩阵可以采用三元组存储。 输入&#xff1a; 输入包含若干个测试用例&#xff0c;每个测试用例的第一行为两个正整数m,n(1<m,n<100),表示矩阵的行数和列数,接下来m行&#xff0c;每行n个整数&#xff0c;表示稀疏…

[NEO解题报告]《Leetcode》1423. 可获得的最大点数 - Rust/C++

文章目录 思路解题方法复杂度CodeRust代码rust 用例 C 代码 Problem: 1423. 可获得的最大点数 思路 两种算法都可以&#xff1a; 方法1&#xff1a; 直接计算&#xff0c; 首尾各自往中间记录两个前缀和&#xff0c; 然后单次遍历 从前面取i个和后面取 k-i 个的和&#xff0c; …

Linux设备树

一、起源 减少垃圾代码 减轻驱动开发工作量 驱动代码和设备信息分离 参考Open Fireware设计 用来记录硬件平台中各种硬件设备的属性信息 二、基本组成 两种源文件&#xff1a; xxxxx.dts dts是device tree source的缩写xxxxx.dtsi dtsi是device tree source include的缩…

HarmonyOS4.0从零开始的开发教程08构建列表页面

HarmonyOS&#xff08;六&#xff09;构建列表页面 List组件和Grid组件的使用 简介 在我们常用的手机应用中&#xff0c;经常会见到一些数据列表&#xff0c;如设置页面、通讯录、商品列表等。下图中两个页面都包含列表&#xff0c;“首页”页面中包含两个网格布局&#xff…

鸿蒙HarmonyOS4.0开发应用学习笔记

黑马程序员鸿蒙4.0视频学习笔记&#xff0c;供自己回顾使用。1.安装开发工具DevEco Studio 鸿蒙harmony开发文档指南 DevEco Studio下载地址 选择或者安装环境 选择和下载SDK 安装总览 编辑器界面 2.TypeScript语法 2.1变量声明 //string 、number、boolean、any、u…

清理oracle库30亿的表后,释放删除空间

1.创建中间表 请以HADES_COD.HDS_COD_BUSI_DETAIL为模板&#xff0c; 手动创建中间表&#xff1a;HADES_COD.HDS_COD_BUSI_DETAIL_1 以及它的constraint约束 示例&#xff1a; -- Create table create table HADES_COD.HDS_COD_BUSI_DETAIL_1 ( id …

HarmonyOS学习--初次下载安装和配置环境

一、Windows下载与安装软件 运行环境要求&#xff1a; 为保证DevEco Studio正常运行&#xff0c;建议电脑配置满足如下要求&#xff1a; 操作系统&#xff1a;Windows10 64位、Windows11 64位内存&#xff1a;8GB及以上硬盘&#xff1a;100GB及以上分辨率&#xff1a;1280*80…

鼎捷受邀出席“中国制造业产品创新数字化国际峰会”,共话工业软件创新发展

11月30日&#xff0c; 由e-works数字化企业网、四川省智能制造创新中心、重庆制信信息技术服务有限公司主办的第十九届中国制造业产品创新数字化国际峰会在四川成都盛大开幕。 作为制造业研发信息化领域规模、影响力兼具的专业论坛&#xff0c;本届峰会以“构建基于数字底座的…

2023年【R2移动式压力容器充装】最新解析及R2移动式压力容器充装模拟考试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年R2移动式压力容器充装最新解析为正在备考R2移动式压力容器充装操作证的学员准备的理论考试专题&#xff0c;每个月更新的R2移动式压力容器充装模拟考试题祝您顺利通过R2移动式压力容器充装考试。 1、【多选题】…

唯创知音WT2003Hx系列单片机语音芯片:家庭理疗产品的智能声音伴侣

随着科技的不断创新&#xff0c;家庭理疗产品正迎来一场智能化的变革。唯创知音的WT2003Hx系列单片机语音芯片以其强大的功能和高品质音频播放能力&#xff0c;为家庭理疗产品带来了更为智能、沉浸式的用户体验。 1. MP3高品质音频播放 WT2003Hx系列语音芯片支持高品质的MP3音…

简单自定义vuex的设计思路

vuex集中式存储管理应用所有组件的状态&#xff0c;并以响应的规则保证状态以可预测的方式 发生变化。 步骤&#xff1a; 1.Store类&#xff0c;保存选项&#xff0c;_mutations&#xff0c;_actions&#xff0c;getters 2.响应式状态&#xff1a;new Vue方式设置响应式。 …

Vue 的异步更新机制是如何实现的

vue 的异步更新机制是如何实现的 实现原理 – 使用事件循环&#xff08;Event Loop&#xff09;和任务队列&#xff08;Task Queue&#xff09;vue 的异步更新机制步骤扩展&#xff1a;this.$nextTick .$nextTick](#扩展thisnexttick) vue 的异步更新机制是如何实现的 实现原…

使用Java8的Stream流的Collectors.toMap来生成Map结构

问题描述 在日常开发中总会有这样的代码&#xff0c;将一个List转为Map集合&#xff0c;使用其中的某个属性为key&#xff0c;某个属性为value。 常规实现 public class CollectorsToMapDemo {DataNoArgsConstructorAllArgsConstructorpublic static class Student {private…

OpenCV-python numpy和基本作图

文章目录 一、实验目的二、实验内容三、实验过程Numpy1.NumPy 操作2.NumPy Ndarray 对象3.NumPy 基本类型4.NumPy 数组属性ndarray.ndimndarray.shapendarray.itemsizendarray.flags 5.NumPy 创建数组numpy.emptynumpy.zerosnumpy.ones 6.NumPy 从已有的数组创建数组numpy.asar…