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)

后边文章中再讲

相关推荐
gqkmiss2 分钟前
Chrome 浏览器 131 版本开发者工具(DevTools)更新内容
前端·chrome·浏览器·chrome devtools
Summer不秃7 分钟前
Flutter之使用mqtt进行连接和信息传输的使用案例
前端·flutter
旭日猎鹰12 分钟前
Flutter踩坑记录(二)-- GestureDetector+Expanded点击无效果
前端·javascript·flutter
Viktor_Ye18 分钟前
高效集成易快报与金蝶应付单的方案
java·前端·数据库
hummhumm20 分钟前
第 25 章 - Golang 项目结构
java·开发语言·前端·后端·python·elasticsearch·golang
乐闻x1 小时前
Vue.js 性能优化指南:掌握 keep-alive 的使用技巧
前端·vue.js·性能优化
一条晒干的咸魚1 小时前
【Web前端】创建我的第一个 Web 表单
服务器·前端·javascript·json·对象·表单
花海少爷1 小时前
第十章 JavaScript的应用课后习题
开发语言·javascript·ecmascript
Amd7941 小时前
Nuxt.js 应用中的 webpack:compiled 事件钩子
前端·webpack·开发·编译·nuxt.js·事件·钩子
生椰拿铁You1 小时前
09 —— Webpack搭建开发环境
前端·webpack·node.js