Kotlin 2.1.0 入门教程(十六)属性、getter、setter、幕后字段、后备属性、编译时常量、延迟初始化

server/2025/2/14 2:20:53/

属性声明

属性可以使用 var 关键字声明为可变的,也可以使用 val 关键字声明为只读的。

kotlin">class Address {var name: String = "Holmes, Sherlock"var street: String = "Baker"var city: String = "London"var state: String? = nullvar zip: String = "123456"
}

要使用这些属性,只需通过属性名来引用它们。

kotlin">fun copyAddress(address: Address): Address {val result = Address()result.name = address.nameresult.street = address.streetreturn result
}

gettersetter

属性声明的完整语法如下:

kotlin">var <propertyName>[: <PropertyType>] [= <property_initializer>][<getter>][<setter>]

这里的初始值设定项、gettersetter 都是可选的。如果属性的类型可以从初始值设定项或者 getter 的返回类型推导出来,那么属性类型也是可选的。

只读属性声明的完整语法和可变属性声明有两点不同:

  • val 开头而非 var

  • 不允许有 setter

kotlin">// 类型为 Int,有默认的 getter,必须在构造函数中初始化。
val simple: Int?// 类型为 Int,有默认的 getter。
val inferredType = 1

你可以为属性定义自定义的访问器。要是定义了自定义的 getter,每次访问该属性时都会调用它(这样就能实现一个计算属性)。下面是一个自定义 getter 的示例:

kotlin">class Rectangle(val width: Int, val height: Int) {// 属性类型可选,因为能从 getter 的返回类型推导出来。val area: Intget() = this.width * this.height
}

如果属性类型可以从获取器 getter 中推断出来,那么你可以省略该属性的类型声明。

kotlin">class Rectangle(val width: Int, val height: Int) {val area get() = this.width * this.height
}

当你定义了一个自定义的 setter 方法后,除了属性初始化的时候,每次给该属性赋值都会调用这个自定义 setter

kotlin">var stringRepresentation: Stringget() = this.toString()set(value) {setDataFromString(value)}

按照惯例,setter 参数的名称是 value,但如果你愿意,也可以选择其他名称。

如果你需要为访问器添加注解或更改其可见性,但又不想改变默认实现,那么可以只定义访问器而不定义其主体:

kotlin">var setterVisibility: String = "abc"private set // 该 setter 是私有的,且采用默认实现。
kotlin">var setterWithAnnotation: Any? = null@Inject set // 为该 setter 添加 Inject 注解。

幕后字段

字段仅作为属性的一部分,用于在内存中存储属性的值。字段不能直接声明。

不过,当属性需要一个幕后字段时,Kotlin 会自动提供。

在访问器中,可以使用 field 标识符来引用这个幕后字段:

kotlin">var counter = 0 // 初始化器会直接为幕后字段赋值。set(value) {if (value >= 0)field = value// 错误,会导致栈溢出:使用实际名称 counter 会使 setter 递归调用。// counter = value}

field 标识符只能在属性的访问器中使用。

如果一个属性至少有一个访问器使用默认实现,或者自定义访问器通过 field 标识符引用它,那么就会为该属性生成一个幕后字段。

例如,在以下情况下就不会生成幕后字段:

kotlin">val isEmpty: Booleanget() = this.size == 0

后备属性

如果你想实现一些无法通过隐式幕后字段机制完成的操作,那么你总可以采用后备属性的方式:

kotlin">private var _table: Map<String, Int>? = null
public val table: Map<String, Int>get() {if (_table == null) {// 类型参数会被自动推断。_table = HashMap()}return _table ?: throw AssertionError("Set to null by another thread")}

JVM 上,对于使用默认 gettersetter 的私有属性,其访问操作会被优化,以避免函数调用带来的开销。

编译时常量

如果一个只读属性的值在编译时就已知,那么可以使用 const 修饰符将其标记为编译时常量。这样的属性需要满足以下要求:

  • 它必须是顶级属性,或者是 object 声明或伴生对象的成员。

  • 它必须用 String 类型或基本数据类型的值进行初始化。

  • 它不能有自定义的 getter

编译器会对常量的使用进行内联处理,将对常量的引用替换为其实际值。不过,该字段不会被移除,因此仍可以通过反射与之交互。

这样的属性也可以用在注解中:

kotlin">const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"@Deprecated(SUBSYSTEM_DEPRECATED) fun foo() { ... }

延迟初始化属性和变量

通常情况下,声明为非空类型的属性必须在构造函数中进行初始化。然而,很多时候这样做并不方便。例如,属性可以通过依赖注入来初始化,或者在单元测试的设置方法中进行初始化。在这些情况下,你无法在构造函数中提供一个非空的初始值,但你仍然希望在类的内部引用该属性时避免进行空检查。

为了处理这种情况,你可以使用 lateinit 修饰符来标记属性:

kotlin">public class MyTest {lateinit var subject: TestSubject@SetUp fun setup() {subject = TestSubject()}@Test fun test() {subject.method()  // 直接解引用。}
}

这个修饰符可以用于类体内部声明的 var 属性(不能用于主构造函数中的属性,并且仅适用于没有自定义 gettersetter 的属性),也适用于顶级属性和局部变量。属性或变量的类型必须是非空类型,并且不能是基本数据类型。

在延迟初始化属性尚未初始化时访问它会抛出一个特殊的异常,该异常会明确指出被访问的属性以及它尚未初始化这一事实。

要检查一个使用 lateinit 修饰的变量是否已经被初始化,可以对该属性的引用使用 .isInitialized

kotlin">if (foo::bar.isInitialized) {println(foo.bar)
}
kotlin">class MyClass {lateinit var myProperty: Stringfun initialize() {myProperty = "First value"}fun update() {myProperty = "Updated value"}fun printProperty() {if (::myProperty.isInitialized) {println(myProperty)}}
}fun main() {val obj = MyClass()obj.initialize()obj.printProperty() // First valueobj.update()obj.printProperty() // Updated value
}

这种检查方式仅适用于那些在词法上可访问的属性,具体来说,这些属性需满足以下条件:它们要么是在同一类型中声明的,要么是在某个外部类型中声明的,又或者是在同一文件的顶级作用域中声明的。


http://www.ppmy.cn/server/167493.html

相关文章

Ubuntu 下 nginx-1.24.0 源码分析 - NGX_HAVE_GETTIMEZONE 宏

表示当前平台支持通过 gettimezone() 直接获取时区偏移值&#xff08;以分钟为单位&#xff09; 该宏用于适配不同操作系统对时区信息获取方式的差异。 当 NGX_HAVE_GETTIMEZONE 被定义时&#xff0c;Nginx 会调用 ngx_gettimezone() 获取时区偏移 在 Ubuntu 环境下&#xff0c…

【Web安全测试】Burp中NEW_xp_CAPTCHA插件(含4.1和4.2)的下载安装和导入

NEW-xp-CAPTCHA插件的下载 首先需准备好以下文件&#xff1a; NEW-xp-CAPTCHA 4.2地址 https://gitcode.com/open-source-toolkit/42f798/?utm_sourcetools_gitcode&indexbottom&typecard NEW-xp-CAPTCHA 4.1地址 NEW-xp-CAPTCHA 4.1python环境下载地址 NEW-xp-…

大数据Orc文件生成与读取

ORC(Optimized Row Columnar)是Hadoop生态系统中一种高效的列式存储文件格式,其主要特性包括高效压缩、快速读取、以及能够存储结构化数据。本文将展示如何使用Java编写代码来生成和读取ORC文件。 一、ORC文件介绍 ORC是一种为Hadoop生态系统优化的列式存储格式,具有以下…

【在线优化】【有源程序】基于遗传算法(GA)和粒子群优化(PSO)算法的MPPT控制策略

目录 一、背景 二、源程序及结果 2.1 simulink仿真程序 2.2 GA模块源程序 2.3 PSO模块源程序 三、程序运行结果 3.1 基于GA优化的MPPT 3.2 基于PSO优化的MPPT 一、背景 MPPT策略能够显著提高光伏、风电等发电效率&#xff0c;节省大量成本。该策略的经典算法是&#xf…

visual studio 2008的试用版评估期已结束的解决办法

visual studio 2008试用期过了后&#xff0c;再次启动时提示&#xff1a;visual studio的试用版评估期已结束。 需要的工具&#xff1a;补丁文件PatchVS2008.exe 解决办法&#xff1a; 1.在“控制面板”-“添加删除程序”中选择visual studio 2008&#xff0c;点击“更改/卸载”…

头条百度批量采集软件说明文档

旧版说明文档《头条号文章批量采集软件4.0版本说明文档&#xff01;头条/微头条文章批量采集》 头条的采集软件已经更新了好多个版本了&#xff0c;一直没有做详细的介绍文档&#xff0c;最近更新了一些功能进去&#xff0c;一块来写一下说明文档。 1、主界面 2、头条作者采集…

安全研究员职业提升路径

阶段一&#xff1a;基础能力沉淀期&#xff08;0-3年&#xff09; 目标薪资&#xff1a;15-30万/年&#xff08;国内&#xff09; 核心技能 掌握渗透测试全流程&#xff08;Web/App/内网&#xff09;熟练使用BurpSuite、Metasploit、IDA Pro等工具理解漏洞原理&#xff08;如O…

自动化办公|xlwings 数据类型和转换

xlwings 数据类型和转换&#xff1a;Python 与 Excel 的桥梁 在使用 xlwings 进行 Python 和 Excel 数据交互时&#xff0c;理解两者之间的数据类型对应关系至关重要。本篇将详细介绍 Python 数据类型与 Excel 数据类型的对应关系&#xff0c;以及如何进行数据类型转换。 一、…