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

定义属性

类属性可使用varval定义

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

属性使用

kotlin 复制代码
fun copyAddress(address: Address): Address {
    val result = Address()
    result.name = address.name 
    result.street = address.street
    // ...
    return result
}

Getters/setters

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

kotlin 复制代码
var <propertyName>[: <PropertyType>] [= <property_initializer>]
    [<getter>]
    [<setter>]

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

[]中的内容都是可选的

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

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

kotlin 复制代码
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,除属性初始化除外,每次属性赋值时都将调用它

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

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

kotlin 复制代码
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关键字表示

kotlin 复制代码
class Rectangle() {
    var test = 4
        set(value) {
            field = value
        }
        get() = field
}

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

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

kotlin 复制代码
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

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

kotlin 复制代码
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

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

kotlin 复制代码
/**
 * 顶级变量
 */
const val SUBSYSTEM_DEPRECATED: String = "方法过期了"

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

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

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

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

kotlin 复制代码
public class MyTest {
    lateinit var subject: TestSubject   // 延迟初始化 subject

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

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

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

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

检查是否已经初始化

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

kotlin 复制代码
class TestSubject() {
    fun method() {}
}

class MyTest {
    lateinit var subject: TestSubject 

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

重写属性(Overriding properties)

下章讲解

委托属性(Delegated properties)

后边文章中再讲

相关推荐
乱码三千9 分钟前
密码管理工具站正式上线啦
前端·html·github
IMPYLH15 分钟前
HTML 的 <rt> 元素
前端·html
单线程_0120 分钟前
从案例分析 Vue3 Tokenizer+Parser 源码五
前端·javascript·vue.js
YB13753 小时前
Kotlin 三类关键字
kotlin
mldong8 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排8 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60619 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角10 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!10 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰10 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端