原创

Kotlin中的多态

Kotlin的变量有两个类型:一个是编译时类型,一个是运行时类型。

  • 编译时类型由声明该变量时使用的类型决定;
  • 运行时类型由实际赋给该变量的对象决定。

一、多态性

open class BaseClass {
    open var book = 6
    fun base() {
        println("父类的普通方法")
    }

    open fun test() {
        println("父类的被覆盖的方法")
    }

}

class SubClass : BaseClass() {
    //重写父类的属性
    override var book = 100

    //重写父类的方法
    override fun test() {
        println("子类的覆盖父类的方法")
    }

    fun sub() {
        println("子类的普通方法")
    }

}

fun main(args: Array<String>) {
    var bc: BaseClass = BaseClass()
    println(bc.book)
    bc.base()
    bc.test()

    var sc: SubClass = SubClass()
    println(sc.book)
    sc.base()
    sc.test()

    var polymophicBc: BaseClass = SubClass()
    println(polymophicBc.book)
    polymophicBc.base()
    polymophicBc.test()
}

输出结果:

6
父类的普通方法
父类的被覆盖的方法
100
父类的普通方法
子类的覆盖父类的方法
100
父类的普通方法
子类的覆盖父类的方法

Kotlin允许把一个子类对象直接赋给一个父类变量,无须任何类型转换,或者被称为向上转型,向上转型由系统自动完成。

二、使用is检查类型

变量只能调用其编译时类型的方法,而不能调用其运行时类型的方法。

如果要让这个变量调用其运行时类型的方法,就需要把它强制转换成运行时类型,强制转换需要借助于强制转型运算符。

Kotlin的类型转换运算符包含asas?

向上转型是由Kotlin自动转换,因此强制转型通常是向下转型。

为了保证类型转换不会出错,Kotlin提供了类型检查运算符:is和!is

import java.util.*

interface Testable {}

fun main(args: Array<String>) {
    val hello: Any = "Hello"
    println("字符串是否是String类的实例:${hello is String}")

    println("字符串是否是Date类的实例:${hello is Date}")

    println("字符串是否是Testable协议的实例:${hello is Testable}")

    val a:String="Hello"
//    println("字符串是否是Date类的实例:${a is Date}")
}

输出结果:

字符串是否是String类的实例:true
字符串是否是Date类的实例:false
字符串是否是Testable协议的实例:false

is运算符的作用是,在进行强制类型转换之前,首先判断前一个变量是否引用后一个类,或者其子类的实例,是否可以成功转换,从而保证代码更加健壮。

只要使用is或!is对变量进行了判断,系统就会自动将变量的类型转换为目标类型。

fun main(args: Array<String>) {
    var a:Any="Hello"

    if (a is String) println(a.length)
}

输出结果:

5

当时用is或!is判断之后,只要Kotlin能推断出变量属于某种类型,就会自动将该变量的编译时类型转换为指定类型。

三、使用as运算符转型

Kotlin提供了两个向下转型运算符:

  • as:不安全的强制转型运算符,如果转型失败,程序将会引发ClassCastException异常。
  • as?:安全的强制转型运算符,如果转型失败,程序不会引发异常,而是返回null。
fun main(args: Array<String>) {
    var a: Any = "Hello"
    //a的编译时类型为Any,Any与String存在继承关系,可以进行转换
    //而且obj实际引用的实例是String类型,所以运行时也可以通过
    val objStr = a as String
    println(objStr)
    //objPri的编译时类型为Any,实际类型为Int
    val objPri: Any = 5
    //Any与String存在继承关系,可以进行转换,编译通过
    //objPri实际引用的实例时Int类型,所以转换失败
    val str: String = objPri as String

    val py = "python"
    val s: String = "Kotlin"
    //s的编译时类型为String,运行时类型为String
    //但是String与Number不存在继承关系,因此编译发出警告:转换不可能成功
    val num: Number = s as Number
}

由于as?转换返回的是可空的值,因此程序需要对as?转换的结果进行null判断。

open class Fruit {
    var name: String
    var weight: Double

    constructor(name: String, weight: Double) {
        this.name = name
        this.weight = weight
    }
}

class Apple : Fruit {
    var color: String

    constructor(name: String, weight: Double, color: String) : super(name, weight) {
        this.color = color
    }
}

class Grape : Fruit {
    var sugarRate: Double

    constructor(name: String, weight: Double, sugarRate: Double) : super(name, weight) {
        this.sugarRate = sugarRate
    }
}

fun main(args: Array<String>) {
    //使用数组保存4个水果
    var fruits = arrayOf(
        Apple("红富士", 1.8, "粉色"),
        Apple("花牛果", 2.3, "红色"),
        Grape("巨峰", 1.4, 0.34),
        Grape("加州提子", 2.2, 0.45)
    )
    for (f in fruits) {

        var ap = f as? Apple
        println("${ap?.name}苹果的颜色是:${ap?.color}")
    }
}

输出结果:

红富士苹果的颜色是:粉色
花牛果苹果的颜色是:红色
null苹果的颜色是:null
null苹果的颜色是:null

学海无涯苦作舟

我的微信公众号.jpg

基本语法
  • 作者:HunterArley (联系作者)
  • 发表时间:2019-11-26 10:09
  • 版权声明:本网站部分内容转载于合作站点或其他站点,但都会注明作/译者和原出处。如有不妥之处,敬请指出。
  • 公众号转载:请在文末添加作者公众号二维码