Android---Kotlin 学习008

定义类

示例:定义一个 Player 类

Kotlin 复制代码
class Player {
    var name = "jack"
}

通过 Show Kotlin Bytecode(按两下 Shift 键),然后点击 Compile

示例2:创建 Player 对象,并给 name 属性赋值

Kotlin 复制代码
class Player {
    var name : String?= "jack"
}

fun main() {
    val p = Player()
    p.name = "rose" // 这种赋值方式其实调用的是 setName() 方法
}

field

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

当我们需要自己定义 get() / set() 方法覆盖自动生成的get() / set() ,就需要用到 field 属性

计算属性

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

Kotlin 复制代码
val rolledValue
    get() = (1..6).shuffled.first()

这里就没有用到 field 。调用 get() 时,(1..6).shuffled.first() 的结果不会影响到 rolledValue。

防范竞态条件

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

示例:

Kotlin 复制代码
    var words : String? = "hello"
    fun saySomething(){
        words?.also { 
            println("Hello ${it.toUpperCase()}")
        }
    }

初始化

主构造函数

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

在 java 中构造函数是与类名同名的,

在主构造函数里定义属性

kotlin 允许你不使用临时变量,而是直接用一个定义同时指定参数和类属性。通常,我们更细化用这种方式定义类属性,因为它会减少代码重复。

次构造函数

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

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

默认参数

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

初始化块

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

Kotlin 复制代码
    init {
        require(age > 0){"age must be positive"}
        require(name.isNotBlank()){"player must have a name."}
    }

初始化顺序

  1. 主构造函数里声明的属性;2. 类级别的属性赋值;3. init 初始化块里的属性赋值和函数调用;4. 次构造函数里的属性赋值和函数调用。

延长初始化

当用到时才初始化。使用 lateinit 关键字相当于做了一个约定:在用它之前负责初始化。

实例:当我们用到 tool 时,才初始化 tool 。即在 main() 方法里调用 ready() 时才初始化。

Kotlin 复制代码
class Student {
    // lateinit 可以使变量延长初始化
    lateinit var tool : String

    fun ready(){
        tool = "pen"
    }
    fun examination(){
        println(tool)
    }
}

fun main() {
    val student = Student()
    // 先 准备 笔
    student.ready()
    // 然后才能考试
    student.examination()
}

如果在 ready() 之前调用 examination(),那么就会抛异常。表示延长初始化变量没有被初始化。

针对上面的异常,只要无法确认 lateinit 变量是否完成初始化,可以执行 isInitialized 检查。

惰性初始化

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

初始化陷阱1

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

示例:

在上面的初始化顺序 中,我们知道类级别属性 是在初始化块属性之前的,所以视乎上面的写法没有太大问题。当时当我们反编译后(show kotlin Bytecoe),发现并不是我们想的那样。

在 konlin 中编译是按从上到下的顺序进行的。所以上面代码中,blood 的声明是在 init{} 块之后,那么这时就会编译出错(此时只是编译,还不是属性初始化,所以不要把二者搞混了)。

正确写法如下:

初始化陷阱2

下面这段代码在编译时没有问题,因为编译器看到 name 属性已经在 init 块 里初始化了,但代码一运行,就会抛出空指针异常,因为 name 属性还没赋值,firstLetter() 函数就应用它了

将 init{} 块的两行代码的先后顺序调换一下,就能解决上述问题:

初始化陷阱3

因为编译器看到所有属性都初始化了,所以代码编译没问题,但运行结果却是 null,问题出在哪?在用 initialPlayerName 函数初始化 playerName 时,name 属性还未完成初始化。

相关推荐
VinRichard2 分钟前
Android 常用三方库
android
Aileen_0v01 小时前
【玩转OCR | 腾讯云智能结构化OCR在图像增强与发票识别中的应用实践】
android·java·人工智能·云计算·ocr·腾讯云·玩转腾讯云ocr
江上清风山间明月4 小时前
Flutter DragTarget拖拽控件详解
android·flutter·ios·拖拽·dragtarget
debug_cat7 小时前
AndroidStudio Ladybug中编译完成apk之后定制名字kts复制到指定目录
android·android studio
编程洪同学11 小时前
Spring Boot 中实现自定义注解记录接口日志功能
android·java·spring boot·后端
氤氲息13 小时前
Android 底部tab,使用recycleview实现
android
tmacfrank13 小时前
Coroutine 基础二 —— 结构化并发(一)
kotlin
Clockwiseee14 小时前
PHP之伪协议
android·开发语言·php
小林爱14 小时前
【Compose multiplatform教程08】【组件】Text组件
android·java·前端·ui·前端框架·kotlin·android studio
小何开发15 小时前
Android Studio 安装教程
android·ide·android studio