优秀的编程知识分享平台

网站首页 > 技术文章 正文

Kotlin学习日记-类属性和字段(Properties/Fields)

nanyue 2024-09-20 21:47:07 技术文章 3 ℃

大家好,欢迎加入阿闯学长的Kotlin学习之旅。

今天来讲kotlin的类属性和字段。

一、声明属性

可以使用var关键字将Kotlin类中的属性声明为可变,或使用val关键字将其声明为只读。

 class Address {
 var name: String = ...
 var street: String = ...
 var city: String = ...
 var state: String? = ...
 var zip: String = ...
}

要使用一个属性,只要用名称引用它即可,就像 Java 中的字段:

fun copyAddress(address: Address): Address {
 val result = Address() // Kotlin 中没有“new”关键字
 result.name = address.name // 将调用访问器
 result.street = address.street
 // ……
 return result
}

二、属性访问器Getters 与 Setters

声明一个属性的完整语法是

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

其初始器(initializer)、getter 和 setter 都是可选的。属性类型如果可以从初始器 (或者从其 getter 返回值,如下文所示)中推断出来,也可以省略。

例如:

var allByDefault: Int? // 错误:需要显式初始化器,隐含默认 getter 和 setter
var initialized = 1 // 类型 Int、默认 getter 和 setter

一个只读属性的语法和一个可变的属性的语法有两方面的不同:

  1. 只读属性的用 val开始代替var
  2. 只读属性不允许 setter
val simple: Int? // 类型 Int、默认 getter、必须在构造函数中初始化
val inferredType = 1 // 类型 Int 、默认 getter

我们可以为属性定义自定义的访问器。如果我们定义了一个自定义的 getter,那么每次访问该属性时都会调用它 (这让我们可以实现计算出的属性)。以下是一个自定义 getter 的示例:

val isEmpty: Boolean
 get() = this.size == 0

如果我们定义了一个自定义的 setter,那么每次给属性赋值时都会调用它。一个自定义的 setter 如下所示:

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

按照惯例,setter 参数的名称是 value,但是如果你喜欢你可以选择一个不同的名称。

自 Kotlin 1.1 起,如果可以从 getter 推断出属性类型,则可以省略它:

val isEmpty get() = this.size == 0 // 具有类型 Boolean

如果你需要改变一个访问器的可见性或者对其注解,但是不需要改变默认的实现, 你可以定义访问器而不定义其实现:

var setterVisibility: String = "abc"
 private set // 此 setter 是私有的并且有默认实现
var setterWithAnnotation: Any? = null
 @Inject set // 用 Inject 注解此 setter

三、幕后字段和属性

1.隐式幕后字段

Kotlin类不能有字段,但有时在自定义的访问器中需要字段,

为此自动提供隐式幕后字段(backing field),可field标识符访问,

field只能用在属性的访问器中.

 var counter = 0 //初始值直接写入到幕后字段
 set(value) {
 if (value >= 0)
 field = value
 }

2.显式幕后属性

如果隐式幕后字段不符合需求,可用显式幕后属性(backing property)

 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")
 }

通过默认getter和setter访问私有属性会被优化,不会引入函数调用开销!

四、属性的初始化

1.编译期常量

属性用const修饰符标记,称为编译期常量Compile-Time Constants,需满足以下要求:

位于顶层或者是object的成员

用 String 或原生类型初始化

没有自定义getter

const val DE: String = "This subsystem is deprecated"
//编译期常量属性可用在注解中
@Deprecated(DE) fun foo(){ 
}

2.延迟初始化属性

一般情况,属性声明为非空类型,必须在构造函数中初始化。

然而这不方便,为此可用lateinit修饰符非空属性:

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

lateinit只能用在类体中的var属性,

该属性不能自定义getter和setter方法,

该属性必须是非空类型,不能是原生类型

Tags:

最近发表
标签列表