----《第一季Kotlin崛起:次世代Android开发 》学习笔记
总目录:每天学一点 Kotlin ---- 目录
上一篇:每天学一点 Kotlin -- 多彩的类:委托1
下一篇:每天学一点 Kotlin -- 对象进阶:异常处理
1. 类型检查与类型转换
1.1 在 Kotlin 中,关键字 is 用来判断传入的类型是否是当前类型或者是它的子类型。这个对应于 Java 中的 instanceof 关键字。
1.2 举个栗子:
open class TypePerson(name: String, age: Int) {
val name = name
val age = age
fun printPersonInfo() {
println("printPersonInfo() -- name = $name, age = $age")
}
}
class TypeStudent(name: String, age: Int, grade: Double) : TypePerson(name, age) {
val grade = grade
fun printStudentInfo() {
println("printStudentInfo() -- name = $name, age = $age, grade = $grade")
}
}
class TypeWorker(name: String, age: Int, salary: Double) : TypePerson(name, age) {
val salary = salary
fun printWorkerInfo() {
println("printWorkerInfo() -- name = $name, age = $age, grade = $salary")
}
}
fun testType1(p: TypePerson) {
if (p is TypePerson) {
p.printPersonInfo()
} else if (p is TypeStudent) {
p.printStudentInfo()
} else if (p is TypeWorker) {
p.printWorkerInfo()
}
}
fun main() {
var p1 = TypePerson("person", 20)
var p2 = TypeStudent("student", 20, 80.8)
var p3 = TypeWorker("worker", 30, 6000.0)
testType1(p1)
testType1(p2)
testType1(p3)
}
打印结果:
printPersonInfo() -- name = person, age = 20
printPersonInfo() -- name = student, age = 20
printPersonInfo() -- name = worker, age = 30
和预想的结果不同,打印结果里面都是父类的方法打印。这是因为这里有一个陷阱,子类实例化后,不但是子类的类型,也是父类的类型。所以方法里面一直在走第一个 if 判断的逻辑。代码修改如下:
fun testType2(p: TypePerson) {
if (p is TypeWorker) {
p.printWorkerInfo()
} else if (p is TypeStudent) {
p.printStudentInfo()
} else if (p is TypePerson) {
p.printPersonInfo()
}
}
fun main() {
var p1 = TypePerson("person", 20)
var p2 = TypeStudent("student", 20, 80.8)
var p3 = TypeWorker("worker", 30, 6000.0)
// testType1(p1)
// testType1(p2)
// testType1(p3)
testType2(p1)
testType2(p2)
testType2(p3)
}
打印结果:
printPersonInfo() -- name = person, age = 20
printStudentInfo() -- name = student, age = 20, grade = 80.8
printWorkerInfo() -- name = worker, age = 30, grade = 6000.0
1.3 编译器对父类和子类之间是可以智能转换的,比如说一个方法中参数是父类,实际传入的是子类,这样是不会报错的,并且调用的方法也会是子类中的方法。
1.4 智能转换时是有规则的:
(1) 对于 val 局部常量总是可以的;
(2) 对于 val 属性,如果属性是 private 或 internal,或者该检查在声明属性的同一模块执行也是可以的,智能转换不适用于 open 的属性或者具有自定义 getter 的属性;
(3) var 局部变量,如果变量在检查和使用之间没有修改,并且没有在会修改它的 lambda 中捕获,也是可以的。
(4) 强调一点:var 属性是绝对不可能被智能转换的,因为这个变量可以随时被其他代码修改。
2. 对象的强制类型转换: as
2.1 也可以手动地进行类型转换。如下列代码中所示:
fun testType3(){
var w : TypePerson = TypeWorker("worker", 45, 300.0)
val w1 = w as TypeWorker
w1.printWorkerInfo()
}
fun main() {
testType3()
}
打印结果:
printWorkerInfo() -- name = worker, age = 45, grade = 300.0
在代码中,虽然是用子类进行实例化,但是声明的却是父类。因此变量 w 是父类类型的。然后在给 w1 赋值时,使用了关键字 as 将变量 w 强制转换为子类类型。之后调用了子类的方法并成功运行。
2.2 再举个栗子:不是父子类之间的转换
fun testType4() {
val s: String = ""
val s1 = s as TypeStudent
s1.printStudentInfo() // java.lang.ClassCastException: class java.lang.String cannot be cast to class chapter07.TypeStudent
}
fun main() {
testType4()
}
打印结果:
java.lang.ClassCastException: class java.lang.String cannot be cast to class TypeStudent
在上面的代码中,运行的时候会报错,因为 String 类型无法转换成 TypeStudent 类型。
2.3 再举个栗子:可空类型之间的转换
(1) 举个栗子1:
fun testType5() {
val w: TypeWorker? = TypeWorker("xiaoMing", 30, 400.0)
val w1 = w as TypeWorker
w1.printWorkerInfo()
}
fun main() {
testType5()
}
打印结果:
printWorkerInfo() -- name = xiaoMing, age = 30, grade = 400.0
(2) 举个栗子2:
fun testType6() {
val w: TypeWorker? = null
val w1 = w as TypeWorker
w1.printWorkerInfo() // java.lang.NullPointerException: null cannot be cast to non-null type chapter07.TypeWorker
}
fun main() {
testType6()
}
打印结果:
java.lang.NullPointerException: null cannot be cast to non-null type chapter07.TypeWorker
因为 TypeWorker? 类型是可以为 null 的,而 TypeWorker 类型不能为 null。由(1)和(2)对比可知:(1)中的变量 w 不为 null,转换是没有问题的;但是在(2)中的变量 w 是 null,因此转换时出错了。
2.4 在使用 as 进行强制类型转换时要注意可空类型, 所以不能保证一个变量不是空的,那么使用 as 反而不方便,不过,可以使用 as? 进行转换。举个栗子:
fun testType7() {
val w: TypeWorker? = null
val w1: TypeWorker? = w
w1?.printWorkerInfo()
}
fun main() {
testType7()
}
打印结果:
打印是空白的。
3. 总结
3.1 随着类的增加和代码量的提升,类型检查和类型转换显得越来越重要,在项目中使用要特别注意呢。