Kotlin学习笔记(五)

本文详细介绍了Kotlin中类的定义、计算属性、初始化顺序、构造函数(包括主构造函数和次构造函数)、默认参数、初始化块、延迟初始化和惰性初始化等核心概念,并通过实例展示了各种初始化陷阱及其解决办法,帮助读者深入理解Kotlin的类和对象初始化机制。
摘要由CSDN通过智能技术生成

目录

定义类

计算属性

防范静态条件

类的初始化

主构造函数

主构造函数中定义属性

次构造函数

 默认参数

初始化块

初始化顺序

 延迟初始化

惰性初始化

初始化陷阱


定义类

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

import kotlin.math.absoluteValue

fun main() {
    var p = Player()
    p.name = " Honey "
    println(p.name)
    p.age = 18
    println(p.age)
}

class Player {
    var name = "Honey"
        get() = field.capitalize()
        set(value) {
            field = value.trim()
        }
    var age = 17
        get() = field.absoluteValue
        set(value) {
            field = value - 1
        }
}

计算属性

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

class Player2 {
    var rolledValue = 0
        get() = (1..66).shuffled().first()
}

防范静态条件

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

class Player3 {
    var words: String? = null
    fun sayWords() {
        words?.also {
            println("Hello,${it.capitalize()}")
        }
    }
}

fun main() {
    val p = Player3()
    p.sayWords()
    p.words = "Kitty"
    p.sayWords()
}

类的初始化

主构造函数

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

class Player(_name: String, _age: Int, _score: Double) {
    var name = _name
    var age = _age
    var score = _score
}

fun main() {
    var p = Player("Honey", 17, 100.0)
    println("${p.name}今年${p.age}岁了,本学期成绩${p.score}分,好棒鸭!")
}

主构造函数中定义属性

class Player(_name: String, var age: Int, var score: Double) {
    var name = _name
}

fun main() {
    var p = Player("Honey", 17, 100.0)
    println("${p.name}今年${p.age}岁了,本学期成绩${p.score}分,好棒鸭!")
}

次构造函数

有主就有次,对应主构造函数的是 次构造函数,我们可以定义多个次构造函数来配置不同的参数组合。

class Player4(_name: String, var age: Int, var score: Double) {
    var name = _name

    constructor(name: String) : this(name, 10, 98.0)
}

fun main() {
    var p2 = Player4("Luffy")
    println("${p2.name}今年${p2.age}岁了,本学期成绩${p2.score}分,好棒鸭!")
}

使用次构造函数也可以定义初始化代码逻辑:

class Player5(_name: String, _age: Int, _score: Double) {
    var name = _name
    var age = _age
    var score = _score

    constructor(_name: String, _age: Int) : this(_name, _age, 60.0) {
        this.name = _name.toUpperCase()
        this.age = _age - 1
        this.score += 6
    }
}

fun main() {
    var p = Player5("Lucky", 18, 92.0)
    println(p.name)
    println(p.age)
    println(p.score)
}

 默认参数

定义构造函数时,可以给构造函数参数指定默认值,如果用户调用时不提供值参,就使用这个默认值。

class Player6(_name: String, var age: Int = 16, var score: Double = 0.0) {
    var name = _name
}

fun main() {
    var p = Player6("Kitty")
    println("${p.name}今年${p.age}岁了,本学期成绩${p.score}分。")
}

初始化块

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

class Student(_name: String, _age: Int) {
    var name = _name
    var age = _age

    init {
        require(age > 0) { "Age is illegal" }
        require(name.isNotBlank()) { "Student must have a name." }
    }
}

fun main() {
    var stu = Student("",-1)
}

初始化顺序

  1. 主构造函数里面声明的属性
  2. 类级别的属性赋值
  3. init初始化块里的属性赋值和函数调用
  4. 次构造函数里的属性赋值和函数调用
class Student2(_name: String, var age: Int) {
    var name = _name
        get() = field.capitalize()
        set(value) {
            field = value.trim()
        }
    var hobby: String
    val subject: String

    init {
        println("initializing ...")
        subject = "math"
        hobby = "listen music"
    }

    constructor(_name: String) : this(_name, 17) {
        hobby = "play guitar"
    }
}

fun main() {
    var stu = Student2("Lucky")
}

 延迟初始化

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

    fun cook() {
        if (::ingredients.isInitialized) println(ingredients)
    }
}

fun main() {
    var chef = Chef()
    chef.ready()
    chef.cook()
}

惰性初始化

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

class LazyInit(_name: String) {
    var name = _name
    val config by lazy { loadConfig() }
    private fun loadConfig(): String {
        println("loading ...")
        return name.capitalize()
    }
}

fun main() {
    val result = LazyInit("luffy")
    println(result.name)
    Thread.sleep(5000)
    println(result.config)
}

初始化陷阱

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

  2. 下面这段代码编译是没有问题的,因为编译器看到name属性已在init中初始化了,但代码运行会抛出空指针异常,因为name属性还没有赋值的时候,firstLetter函数就把name拿去用了。
    class InitTrap {
        val name: String
        private fun firstLetter() = name[0]
    
        init {
            println(firstLetter())
            name = "Honey"
        }
    }
    
    fun main() {
        val initTrap = InitTrap()
    }
  3. 因为编译器看到所有属性都初始化了,所以代码编译没问题,但运行结果却是null,问题出在哪?在用initPlayerName函数初始化playerName时,name属性还未完成初始化。
     
    class InitTrap(_name: String) {
        val playerName: String = initName()
        var name = _name
        private fun initName() = name
    
    }
    
    fun main() {
        val initTrap = InitTrap("Honey")
        println(initTrap.playerName)
    }

一定要爱着点什么,它让我们变得坚韧,宽容,充盈,无惧无畏,甚至有时候让别人觉得我们迷之自信。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值