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 属性还未完成初始化。

相关推荐
zhangphil1 小时前
Android GPU的RenderThread Texture upload上传Bitmap优化prepareToDraw
android
柿蒂2 小时前
聊聊SliverPersistentHeader优先消费滑动的设计
android·flutter
假装多好1234 小时前
android三方调试几个常用命令
android·1024程序员节·三方,gms
侧耳4294 小时前
android11禁止安装apk
android·java·1024程序员节
JohnnyDeng945 小时前
ArkTs-Android 与 ArkTS (HarmonyOS) 存储目录全面对比
android·harmonyos·arkts·1024程序员节
2501_915918415 小时前
iOS 26 查看电池容量与健康状态 多工具组合的工程实践
android·ios·小程序·https·uni-app·iphone·webview
limingade6 小时前
手机摄像头如何识别体检的色盲检查图的数字和图案(下)
android·1024程序员节·色盲检查图·手机摄像头识别色盲图案·android识别色盲检测卡·色盲色弱检测卡
文火冰糖的硅基工坊6 小时前
[嵌入式系统-150]:智能机器人(具身智能)内部的嵌入式系统以及各自的功能、硬件架构、操作系统、软件架构
android·linux·算法·ubuntu·机器人·硬件架构
2501_915909067 小时前
iOS 架构设计全解析 从MVC到MVVM与使用 开心上架 跨平台发布 免Mac
android·ios·小程序·https·uni-app·iphone·webview
明道源码8 小时前
Android Studio 创建 Android 模拟器
android·ide·android studio