Kotlin 属性与字段、密封类、数据类

137 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第25天,点击查看活动详情

一、属性与字段

1)Kotlin 中声明一个完整的属性方式语法结构如下:

// 可变属性
var <propertyName>[: <PropertyType>] [= <property_initializer>]
    [<getter>]
    [<setter>]

// 只读属性
val <propertyName>[: <PropertyType>] [= <property_initializer>]
    [<getter>]

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

Kotlin 中没有给类型属性添加默认值,在我们没有使用特殊修饰符的情况下,我们应该对其进行赋初始值

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

//可读属性
val simple: Int? // 错误: 需要显示初始化器 类型 Int、默认 getter
val inferredType = 1 // 类型 Int 、默认 getter

2)自定义 getter & setter

//自定义 getter
class FieldClass {
    var name: String = "1"
    get() = "erdai"

}

fun main() {
    val field = FieldClass()
    println(field.name)
}
//打印结果
erdai

//自定义 setter
class FieldClass {
  
    var name: String = "1"
    set(value) {
        println("value is $value")
    }

}

fun main() {
    val field = FieldClass()
    field.name = "erdai"
    println(field.name)
}
//打印结果
value is erdai
1

如果对上述打印结果有疑问,可能是你还不了解幕后字段。

3)、幕后字段,在Kotlin中, 如果属性只有一个访问器使用默认实现,那么Kotlin会自动提供幕后字段,用关键字 field 表示,幕后字段主要用于自定义 getter 和 setter 中,并且只能在 getter 和 setter 中访问

class FieldClass {
  
    var name: String = "1"
    set(value) {
      	field = value
        println("value is $value")
    }

}

fun main() {
    val field = FieldClass()
    field.name = "erdai"
    println(field.name)
}
//打印结果
value is erdai
erdai

4)、幕后属性,对外表现为只读,对内表现为可读可写,我们将这个属性称为幕后属性

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

二、密封类

密封类可以解决多余分支,避免我们写出永远不执行的代码,使我们的代码更加规范和安全

1)、使用 sealed class 定义一个密封类

2)、密封类及其子类,只能定义在同一个文件的顶层位置

3)、密封类可被继承

4)、当我们使用条件语句的时候,需要实现密封类所有子类的情况,避免写出永远不会执行的代码

//情况1:使用接口
interface Season
class Spring : Season
class Summer : Season
class Autumn : Season
class Winter : Season

//下面这句 else 分支毫无意义
fun getSeasonName(season: Season) = when (season) {
    is Spring -> "春"
    is Summer -> "夏"
    is Autumn -> "秋"
    is Winter -> "冬"
    else -> throw RuntimeException()
}

fun main() {
   print(getSeasonName(Spring()))
}
//打印结果//情况2:使用密封类
//密封类
sealed class Season
class Spring : Season()
class Summer : Season()
class Autumn : Season()
class Winter : Season()

fun getSeasonName(season: Season) = when (season) {
    is Spring -> "春"
    is Summer -> "夏"
    is Autumn -> "秋"
    is Winter -> "冬"
}
fun main() {
   print(getSeasonName(Summer()))
}
//打印结果

三、数据类

1)、数据类会自动给我们提供 equals( ),hashCode( ),toString( ) 等方法

2)、声明一个数据类,使用 data class 修饰

注意事项

1)、数据类至少得有一个属性,且必须使用 val 或 var 关键字声明

data class SomeSpecificClass4(val attr: String)

四、总结

本篇文章我们介绍了:

1、Kotlin 中的属性与字段,注意幕后字段和幕后属性的概念

2、使用密封类解决多余的分支

3、数据类的介绍及定义

好了,本篇文章到这里就结束了,希望能给你带来帮助 🤝