目录

定义类与field关键字

计算属性与防范竞态条件

主构造函数

在主构造函数里定义属性

次构造函数

默认参数

初始化块

初始化顺序

延迟初始化 lateinit

惰性初始化

初始化陷阱


定义类与field关键字

  • 定义类
class Player{
    var name:String? = "hello"
}

fun main(){
    var player = Player()
    player.name = "d"
    println(player.name)   //d
}

感觉和python很相似。

  • field关键字

针对你定义的每一个属性,Kotlin都会产生一个field,一个getter,以及一个setter,field用来存储属性数据,你不能直接定义field,Kotlin会封装field,保护它里面的数据,只暴露给getter和setter使用。属性的getter方法决定你如何读取属性值,每个属性都有getter方法,setter方法决定你如何给属性赋值,所以只有可变属性才会有setter方法,尽管Kotlin会自动提供默认的getter和setter方法,但在需要控制如何读写属性数据时,你也可以自定义他们。

class Player{
    var name:String = "hello"
        get() = field.capitalize()  //Hello
        set(value){
            field = value.trim()  //去掉空格
        }
}

fun main(){
    var player = Player()
    println(player.name)   //调用get()   Hello
    player.name = " rose "  //调用set()  rose去掉空格
    println(player.name)    //get() Rose
}

自定义,get时首字母大写,set时,去掉空格。

计算属性与防范竞态条件

  • 计算属性

计算属性是通过一个覆盖的get或set运算符来定义,这时field就不需要了。

  • 防范竞态条件

如果一个类属性既可空又可变,那么引用它之前你必须保证它非空,一个办法是用also标准函数。

private class Player{
    val words:String ? = "hello"
    fun saySomething(){
        words?.also { println(it) }
    }
}

主构造函数

我们在Player类的定义头中定义一个主构造函数,使用临时变量为Player的各个属性提供初始值,在Kotlin中,为了便于识别,临时变量(包括仅引用一次的参数),通常都会以下划线开头的名字命名。

class Player2(
    _name: String,
    _age: Int,
    _isNormal:Boolean
){
    var name: String = _name
        get() = field.capitalize()
        set(value){                //可以private 让set方法不可见
            field = value.trim()
        }

    var age: Int = _age
    var isNormal:Boolean = _isNormal
}

fun main(){
    var P = Player2("ph",18,false)
    println(P.name)  //Ph
    println(P.age)   //18
}

在主构造函数里定义属性

class Player3(
    _name: String,
    var age: Int ,
    var isNormal:Boolean
){
    var name: String = _name
        get() = field.capitalize()
        set(value){                //可以private 让set方法不可见
            field = value.trim()
        }
}

fun main(){
    var P = Player3("ph",18,false)
    println(P.name)  //Ph
    println(P.age)   //18
}

次构造函数

  • 使用次构造函数传参
class Player3(
    _name: String,
    var age: Int ,
    var isNormal:Boolean
){
    var name: String = _name
        get() = field.capitalize()
        set(value){                //可以private 让set方法不可见
            field = value.trim()
        }

    constructor(name:String):this(name,0,false)  //次构造函数
}

fun main(){
    var P = Player3("ph",18,false)  //主构造函数构造
    var P2 = Player3("ph")    //次构造函数构造
    
    println(P.name)  //Ph
    println(P.age)   //18
}

一个可以调用主构造函数的构造函数。

  • 使用次构造函数,定义初始化代码逻辑

次构造函数内部还可以干点儿事儿。

class Player3(
    _name: String,
    var age: Int ,
    var isNormal:Boolean
){
    var name: String = _name
        get() = field.capitalize()
        set(value){                //可以private 让set方法不可见
            field = value.trim()
        }

    constructor(name:String):this(name,0,false){   //次构造函数
        this.name = name.uppercase()   //大写
    }
}

fun main(){
    var P = Player3("ph",18,false)  //主构造函数构造
    var P2 = Player3("ph")    //次构造函数构造

    println(P2.name)  //PH
}

在次构造函数内部实现将name转为大写。

默认参数

太简单啦

class Player3(
    _name: String,
    var age: Int = 10,
    var isNormal:Boolean = false
){
    ....
}

初始化块

初始化块可以设置变量或值,以及执行有效性检查,如检查传给某构造函数的值是否有效,初始化代码会在构造类实例时执行。

class Player3(
    _name: String,
    var age: Int = 10,
    var isNormal:Boolean = false
){
    var name: String = _name
        get() = field.capitalize()
        set(value){                //可以private 让set方法不可见
            field = value.trim()
        }

    constructor(name:String):this(name,0,false){   //次构造函数
        this.name = name.uppercase()   //大写
    }

    //初始化块
    init{
        require(age > 0) { "age must be > 0" }
        require(name.isNotBlank()) { "player name must not be blank" }

    }
}

fun main(){
    var P = Player3("",18,false)  //主构造函数构造
    //初始化实例时,init报错,因为name为空
}

初始化块去检查name,age

初始化时传入name为空:

初始化顺序

主构造函数里声明的属性——>

类级别的属性赋值——>

init初始化块里的属性赋值和函数调用——>

次构造函数里的属性赋值和函数调用

延迟初始化 lateinit

  • 使用lateinit关键字相当于做了一个约定:在用它之前负责初始化
  • 只要无法确认lateinit变量是否完成初始化,可以执行isInitialized检查
class Player4{
    lateinit var equipment: String   //延迟初始化
    fun ready(){
        equipment = "knife"
    }

    fun battle(){
        if(::equipment.isInitialized) {   //安全检查
            println(equipment)
        }
    }
}

fun main(){
    val p = Player4()
    p.ready()
    p.battle()
}

惰性初始化

延迟初始化并不是推后初始化的唯一方式,你也可以暂时不初始化某个变量,直到首次使用它,这个叫做惰性初始化。


class Player5(_name: String){
    var name =  _name

    val config by lazy {loadConfig()}
    private fun loadConfig():String{
        println("Loading config")
        return "xxx"
    }
}

fun main(){
    val p = Player5("John")
    Thread.sleep(3000)
    println(p.config)    
}

初始化陷阱

在使用初始化块时,顺序非常重要,你必须保证块中的所有属性已完成初始化。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐