13:kotlin类和对象 -- 属性(Properties)

定义属性

类属性可使用varval定义

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

属性使用

fun copyAddress(address: Address): Address {
    val result = Address()
    result.name = address.name 
    result.street = address.street
    // ...
    return result
}

Getters/setters

完整的定义一个属性的公式如下

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

val <propertyName>[: <PropertyType>] [= <property_initializer>]
    [<getter>]

[]中的内容都是可选的

var initialized = 1 // 有类型Int, 默认getter和默认setter
val simple: Int? // 有类型Int, 默认getter, 必须在构造函数中初始化
val inferredType = 1 // 类型Int,默认getter

如果定义了getter,则每次访问该属性时都会调用

class Rectangle(val width: Int, val height: Int) {
    val area: Int // 属性Int在这里是可选的,因为可以从getter中推断出类型
        get() = this.width * this.height
}

val area get() = this.width * this.height

如果自定义了setter,除属性初始化除外,每次属性赋值时都将调用它

var stringRepresentation: String
    get() = this.toString()
    set(value) {
        setDataFromString(value) // 解析字符串并把值赋给其他字段
    }

如果想要一个var类型的属性不能在外部被修改,可以使用private或者@Inject修饰setter方法

var setterVisibility: String = "abc"
    private set 

var setterWithAnnotation: Any? = null
    @Inject set // 官方说可以使用这个,但是未找到这个怎么用,说找不到这个注解

Backing fields

我称为隐藏字段

官方介绍是这样说的,让我觉得头大

In Kotlin, a field is only used as a part of a property to hold its value in memory. Fields cannot be declared directly. However, when a property needs a backing field, Kotlin provides it automatically. This backing field can be referenced in the accessors using the field identifier

我理解的意思是:在kotlin中,字段(field)并不是类中定义的属性,而是用来存属性值的一个东西。不能手动定义字段,当在访问器中需要用到字段的时候,kotlin已经为我们自动的定义好了,直接用就行了,字段用field关键字表示

class Rectangle() {
    var test = 4
        set(value) {
            field = value
        }
        get() = field
}

实例中,field,也就是字段,指向存储4的位置,而不是test

如果在访问器中不使用field,而是使用属性本身会怎样

class Rectangle() {
    var test = 4
        get() = test
}

fun main() {
    val rectangle = Rectangle()
    println(rectangle.test)
}

在这里插入图片描述
idea提示这是一个递归调用,第13行也标志是一个递归调用。如果执行了代码则会报错Exception in thread "main" java.lang.StackOverflowError,这是因为在访问器中使用了属性名,而使用属性名会调用访问器,循环递归后内存溢出,所以需要使用field来代指属性,避免这个问题,就目前看field更像一个补丁

Backing properties

如果做一些不适合隐藏字段的事情,可以使用隐藏属性

class Rectangle() {
    private var _table: Map<String, Int>? = null
    val table: Map<String, Int>
        get() {
            if (_table == null) {
                _table = HashMap() // Type parameters are inferred
            }
            return _table ?: throw AssertionError("Set to null by another thread")
        }
}

在JVM上:使用默认gettersetter访问私有属性做了优化,以避免函数调用开销。

编译时常量(Compile-time constants)

如果只读属性的值在编译时已知,则使用const修饰符将其标记为编译时常量。此类属性需要满足以下要求:

  • 它必须是顶级属性,或者是对象或伴生对象的成员。
  • 它必须使用String或原始类型的值进行初始化
  • 它不能是自定义getter

编译器将内联常量的用法,将对常量的引用替换为其实际值。但是,该字段不会被删除,因此可以使用反射与之交互

/**
 * 顶级变量
 */
const val SUBSYSTEM_DEPRECATED: String = "方法过期了"

class Demo() {
    @Deprecated(SUBSYSTEM_DEPRECATED) // 注解中可使用const修饰的属性
    fun foo() { }
}

延迟初始化属性和变量(Late-initialized properties and variables)

通常,声明为非空类型的属性必须在构造函数中初始化。然而,这样做并不方便。例如,属性可以通过依赖注入进行初始化,或者在单元测试的设置方法中进行初始化。

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

public class MyTest {
    lateinit var subject: TestSubject   // 延迟初始化 subject

    @SetUp fun setup() {
        subject = TestSubject()
    }

    @Test fun test() {
        subject.method()
    }
}

可以用在类的主体内声明的var属性(不在主构造函数中,且没有自定义的getter或setter时),以及顶层属性和局部变量。属性或变量的类型必须是非空的,并且不能是原始类型

在初始化之前访问lateinit属性会抛出一个特殊的异常,清楚地标识被访问的属性以及它尚未被初始化的事实

检查是否已经初始化

使用.isInitialized方法检查是否已经初始化

class TestSubject() {
    fun method() {}
}

class MyTest {
    lateinit var subject: TestSubject 

    fun test() {
        if (this::subject.isInitialized)
            subject.method()
    }
}

重写属性(Overriding properties)

下章讲解

委托属性(Delegated properties)

后边文章中再讲

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

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

相关文章

Dynamsoft Barcode Reader教程:如何使用Dynamsoft Java条形码阅读器扫描多个条形码

目前有许多开源和商业条形码SDK&#xff0c;但只有少数可以通过扫描一次来识别多个条形码。当您在Google中搜索条形码SDK或Java条形码SDK时&#xff0c;您会发现Dynamsoft Barcode Reader SDK始终位于搜索结果的前5位。在本文中&#xff0c;我将分享如何使用Dynamsoft Java条码…

【Linux】tar 命令使用

tar 命令 tar&#xff08;英文全拼&#xff1a;tape archive &#xff09;命令用于备份文件。tar 是用来建立&#xff0c;还原备份文件的工具程序&#xff0c;它可以加入&#xff0c;解开备份文件内的文件。 著者 由约翰吉尔摩和杰伊芬拉森撰写。 语法 tar [选项] [压缩后文…

linux用户组管理_创建删除密码修改

2.2 用户/组管理 2.2.1 用户 2.2.1.1 useradd&#xff1a;创建用户 添加用户账号就是在系统中创建一个新账号&#xff0c;然后为新账号分配用户号、用户组、主目录和登录Shell等资源。 useradd命令的基本语法如下&#xff1a; useradd 选项 用户名 常见选项参数&#xff…

基于UDP的网络聊天室

客户端 #include <myhead.h> //定义存储信息结构体 typedef struct _MSG {char code; //操作码&#xff1a;L表示登录C表示群聊S表示系统消息S表示退出char name[128]; char txt[256];}msg_t;//定义保存客户端网络信息的链表 typedef struct _ADDR {struct sockaddr_i…

深度学习毕设项目 深度学习 python opencv 动物识别与检测

文章目录 0 前言1 深度学习实现动物识别与检测2 卷积神经网络2.1卷积层2.2 池化层2.3 激活函数2.4 全连接层2.5 使用tensorflow中keras模块实现卷积神经网络 3 YOLOV53.1 网络架构图3.2 输入端3.3 基准网络3.4 Neck网络3.5 Head输出层 4 数据集准备4.1 数据标注简介4.2 数据保存…

从0开始学习JavaScript--JavaScript 模板字符串的全面应用

JavaScript 模板字符串是 ES6 引入的一项强大特性&#xff0c;它提供了一种更优雅、更灵活的字符串拼接方式。在本文中&#xff0c;将深入探讨模板字符串的基本语法、高级用法以及在实际项目中的广泛应用&#xff0c;通过丰富的示例代码带你领略模板字符串的魅力。 模板字符串…

MySQL InnoDB Cluster

MySQL InnoDB Cluster 一、InnoDB Cluster 基本概述 MySQL InnoDB Cluster 为 MySQL 提供了一个完整的高可用解决方案。通过使用 MySQL Shell 提供的 AdminAPI,你可以轻松地配置和管理一组至少由3个MySQL服务器实例组成的 InnoDB 集群。 InnoDB 集群中的每个 MySQL 服务器实例…

IDEA专栏—重装IDEA的配置

文章目录 1、maven路径2、默认文件路径3、插件4、导包顺序5、快捷键6、调整配置插件 1、maven路径 2、默认文件路径 3、插件 4、导包顺序 import static all other imports <blank line> import java.* import javax.* <blank line> import all other imports <…

基于Java SSM框架实现KTV点歌系统项目【项目源码+论文说明】计算机毕业设计

基于java的SSM框架实现KTV点歌系统演示 摘要 本论文主要论述了如何使用JAVA语言开发一个KTV点歌系统&#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论述KTV点歌系…

手动创建映像及在OpenStack云计算平台的镜像应用

目录 一、下载 rhel7.6 安装ISO 二、在VMware 的虚拟机内创建虚拟机 三、更改一些设置 1、使用httpd暴露&#xff08;在外部虚拟机&#xff09; 2、添加软件仓库 3、 安装 ACPI 服务 4、使用 cloud-init 获取公钥 5、安装 cloud-utils-growpart 以允许调整分区大小 6、…

面试:说一下深拷贝,浅拷贝,引用拷贝吧;Object类中的clone是哪种呢?

目录 深拷贝、浅拷贝、引用拷贝Object类的clone()方法 深拷贝、浅拷贝、引用拷贝 ● 浅拷贝&#xff1a; 对基本数据类型进行值传递&#xff1b; 对引用类型&#xff0c;复制了一份引用类型的变量 里面存储的内存地址一样 指向的对象也一样。 ● 深拷贝&#xff1a;对基本数据…

postgres在docker中使用

记录个人开发过程中postgres在docker中的使用&#xff0c;以便后续查看。 Dockerfile 个人是在M1电脑上开发&#xff0c;所以platform使用linux/amd64来兼容amd芯片。 FROM --platformlinux/amd64 postgres:16.1-alpine COPY ./poetrydb.sql /docker-entrypoint-initdb.d/po…

Java多线程核心技术一-基础篇synchronzied同步语句块

接上篇&#xff1a;Java多线程核心技术二-synchronzied同步方法 1 概述 用synchronzied关键字声明方法在某些情况下是有弊端的&#xff0c;比如A线程调用同步方法执行一个长时间的任务&#xff0c;那么B现成就要等待比较长的时间&#xff0c;此时可以使用synchronzied同步语句…

nexus 制品库管理

目录 一、nexus 介绍 二、nexus 支持的仓库 三、nexus 部署 四、nexus 数据备份 五、创建一个内网yum源 六、创建一个代理yum仓库 七、jenkins 使用 nexus插件 7.1 jenkins 安装插件 7.2 配置 maven 工程 7.3 查看构建和上传 一、nexus 介绍 Nexus 是一个强大的仓库管…

Linux创建与编辑视图

本博客将会详细讲解如何在Linux中如何编辑配置文件 输出重定向 对于一台设备而言&#xff0c;存在着两种设备&#xff0c;分别负责输入与输出&#xff1a; 显示器&#xff08;输出设备>&#xff09; 与 键盘&#xff08;输入设备<&#xff09; 对于Linux系统而言&#…

存储过程与触发器的练习题

1&#xff0e;实验目的 掌握使用SQL Server管理平台和Transact-SQL语句创建存储过程、执行存储过程、修改存储过程、删除存储过程的用法。理解使用SQL Server管理平台和Transact-SQL语句查看存储过程定义、重命名存储过程的用法。掌握通过SQL Server管理平台和Transact-SQL语句…

【古月居《ros入门21讲》学习笔记】18_常用可视化工具的使用

目录 说明&#xff1a; 1. Qt工具箱 日志输出工具&#xff1a;rqt_console 绘制数据曲线&#xff1a;rqt_plot 图像渲染工具&#xff1a;rqt_image_view 综合工具&#xff1a;rqt 2. 三维可视化工具&#xff1a;Rviz Rviz启动 使用示例 3. 仿真平台&#xff1a;Gazebo…

通用plantuml模板头

通用plantuml文件 startuml participant Admin order 0 #87CEFA // 参与者、顺序、颜色 participant Student order 1 #87CEFA participant Teacher order 2 #87CEFA participant TestPlayer order 3 #87CEFA participant Class order 4 #87CEFA participant Subject order …

004:Direct 2D离屏渲染(Qt中实现)

简介&#xff1a; 用QT开发图像显示的小程序&#xff0c;需要一些标注工具&#xff0c;由于用的是opengl渲染&#xff0c;所以就在内存中进行绘制&#xff0c;然后纹理贴图贴出去&#xff0c;发现Qt绘制的效果太差&#xff0c;且速度一般&#xff0c;于是就想着用direct2d来绘制…

MySQL根据binlog恢复数据

简介 本文介绍了使用mysqlbinlog导出数据&#xff0c;根据binlog恢复数据&#xff0c;和导出数据时报需要super权限的解决方法。 环境 MySQL: 5.7.40 说明 MySQL的binlog是数据库服务器在运行过程中产生的日志文件&#xff0c;记录了数据库增删改的操作&#xff0c;可用于恢复和…