每天学一点 Kotlin -- 对象进阶:对象类型

----《第一季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 随着类的增加和代码量的提升,类型检查和类型转换显得越来越重要,在项目中使用要特别注意呢。

相关代码:https://gitee.com/fzq.com/test-demo

你可能感兴趣的:(每天学一点 Kotlin -- 对象进阶:对象类型)