美文网首页
Kotlin抽象类与接口 (2)✔️接口使用

Kotlin抽象类与接口 (2)✔️接口使用

作者: 狼性代码人 | 来源:发表于2019-06-03 18:46 被阅读0次
    • 接口声明和实现
    • 接口与多继承
    • 接口继承
    • 接口中的具体函数和属性
    • ✔️同一函数继承多个实现的问题
    • 比抽象类更加抽象的是接口,接口包括 抽象函数和抽象属性 ,也可以根据需要有 具体的函数具体属性(没有‘field’)

    注意:接口和抽象类都可以有抽象函数和抽象属性,也可以有具体函数和具体属性。那么接口和抽象类有什么区别?接口不能维护一个对象状态,而抽象类可以,因为维护一个对象状态需要支持字段,而接口中无论是具体属性还是抽象属性,后面都 没有支持字段

    // 抽象类
    abstract class Figure {
        abstract fun onDraw()       // 抽象函数,无函数体
        abstract val name: String   // 抽象属性,无初始值,无getter和setter访问器
    
        var cname: String = "几何图形"  // 具体属性 1️⃣
            get() = field
            set(value) {
                field = "Figure -> $value"
            }
    
        fun display() = println(name)  // 具体函数
    }
    
    // 接口
    interface Figure {
        fun onDraw()       // 抽象函数,无函数体
        val name: String   // 抽象属性,无初始值,无getter和setter访问器
    
        var cname: String    // 3️⃣具体属性
            get() = "几何图形"
            set(value) {
                // 逻辑体
            }
    
        //var cname: String = "几何图形"  // 2️⃣编译错误,接口不具有支持字段 field
        //    get() = field
        //    set(value) {
        //        field = "Figure -> $value"
        //    }
    
        fun display() = println(name)  // 具体函数
    }
    
    • 代码第1️⃣行是在抽象类中声明了一个具体属性 cname,且可以正常编译通过;

    • 代码第2️⃣行是在接口中声明了一个具体属性 cname,编译不通过,因为没有 支持字段(field) 存储状态。

    • 接口中的声明具体属性,不能有初始值,并且 gettersetter 访问器中不能使用支持字段 field。例如上面代码第3️⃣行。

    一、接口声明和实现

    在 kotlin 中接口声明使用的关键字是 interface。接口中可以定义 0 - N 个抽象函数和属性,也可以定义 0 - N 个具体函数和属性。

    interface 接口名 {
        fun 函数名(参数列表): 返回值类型    // 抽象函数,可有可无
        var|val 属性名: 属性类型          // 抽象属性,可有可无
      
        fun 函数名(参数列表): 返回值类型 {   // 具体函数,可有可无
            // 函数体
        }
    
        val 属性名: 属性类型               // 具体属性,可有可无
            get() {
                // 不能访问 feild
                return 具体类型实例
            }
            set(value) {
                // 逻辑体
                // 不能访问 feild
            }
    }
    

    例如声明一个 Figure 接口的示例:

    interface Figure { // 4️⃣
        fun onDraw()       // 5️⃣抽象函数,无函数体
        val name: String   // 6️⃣抽象属性,无初始值,无getter和setter访问器
    
        val cname: String   // 7️⃣具体属性
            get() = "几何图形"
    
        fun display() = println(name)  // 8️⃣具体函数
    }
    
    • 代码第4️⃣行是声明Figure接口,声明接口使用 interface 关键字。

    • 代码第5️⃣行是声明抽象函数,抽象函数没有函数体。

    • 代码第6️⃣行是声明抽象属性,抽象属性没有初始值,没有 gettersetter 访问器。

    • 代码第7️⃣行的属性,是具体属性,具体属性不能有初始值,并且 gettersetter 访问器中不能使用支持字段 field。

    • 代码第8️⃣行的函数,是具体函数,它有函数体。

    提示:具体函数和属性,可以理解为抽象函数和属性的默认实现。

    class Ellipse : Figure {
    
        override val name: String
            get() = "椭圆形"
    
        override fun onDraw() {
            println("绘制椭圆形...")
        }
    
        override fun display() {
            println("Ellipse -> $name")
        }
    }
    
    class Triangle(override val name: String) : Figure {
    
        override var cname: String
            get() = "Triangle -> $name"
            set(value) { }
    
        override fun onDraw() {
            println("绘制三角形...")
        }
    }
    
    class Rectangle : Figure {
    
        override val name: String = "矩形"
    
        override fun onDraw() {
            println("绘制矩形...")
        }
    }
    
    fun main(args: Array<String>) {
        val f1 = Ellipse()
        f1.onDraw()
        f1.display()
        println(f1.cname)
    
        val f2 = Triangle("三角形")
        f2.onDraw()
        f2.display()
        println(f2.cname)
    }
    
    2019-06-03 17:03:49.315 15906-15906/cn.ak.kot I/System.out: 绘制椭圆形...
    2019-06-03 17:03:49.315 15906-15906/cn.ak.kot I/System.out: Ellipse -> 椭圆形
    2019-06-03 17:03:49.316 15906-15906/cn.ak.kot I/System.out: 几何图形
    2019-06-03 17:03:49.318 15906-15906/cn.ak.kot I/System.out: 绘制三角形...
    2019-06-03 17:03:49.318 15906-15906/cn.ak.kot I/System.out: 三角形
    2019-06-03 17:03:49.318 15906-15906/cn.ak.kot I/System.out: Triangle -> 三角形
    

    注意:接口和抽象类一样,都不能被实例化。

    二、接口与多继承

    在 kotlin 中只允许继承一个类,但可以实现多个接口。通过实现多个接口的方法满足多继承的设计需求。

    interface InterfaceA {
        fun methodA()
        fun methodB()
    }
    
    interface InterfaceB {
        fun methodB()
        fun methodC()
    }
    
    class AB: Any(), InterfaceA, InterfaceB {
        override fun methodA() {
            println("methodA()")
        }
    
        override fun methodB() { // 9️⃣
            println("methodB()")
        }
    
        override fun methodC() {
            println("methodC()")
        }
    }
    
    • 代码第9️⃣行是即实现了接口 InterfaceA 的抽象方法,也实现了接口 InterfaceB 的抽象方法。

    三、接口继承

    • kotlin 中允许接口和接口之间继承。由于接口中的函数都是抽象函数,没有函数体,所以继承之后也不需要额外做什么。
    • 接口继承时使用 (:) 声明,当继承多个接口时使用 (,) 分隔。
    interface InterfaceA {
        fun methodA()
    }
    
    interface InterfaceB {
        fun methodB()
    }
    
    interface InterfaceC : InterfaceA, InterfaceB {
        override fun methodB() // 🔟
        fun methodC()
    }
    
    class ABC : InterfaceC {
        override fun methodA() {
            println("methodA()")
        }
    
        override fun methodB() {
            println("methodB()")
        }
    
        override fun methodC() {
            println("methodC()")
        }
    }
    

    代码第🔟行,接口 InterfaceC 中的 methodB() 重写了接口 InterfaceB,事实上在接口中重写抽象函数并没有实际意义,因为它们都是抽象的,都要留给子类实现的。

    四、接口中的具体函数和属性

    在 kotlin 中接口的主要成员是抽象函数和抽象属性,但是也有具体函数和具体属性。接口中的抽象函数和抽象属性是必须要实现的,而具体函数和具体属性是可选择实现的。

    // 定义接口InterfaceA
    interface InterfaceA {
        fun methodA()
        fun methodB(): Int
    
        fun methodC(): String {
            return "InterfaceA -> methodC"
        }
    
        fun methodD(): Int = 0
    }
    
    // InterfaceA实现类
    class InterfaceAImpl : InterfaceA {
        override fun methodA() { }
    
        override fun methodB(): Int {
            return 0
        }
    
        override fun methodC(): String {
            return "InterfaceAImpl -> methodC"
        }
    }
    

    InterfaceAImpl 必须实现接口 InterfaceA 中的抽象方法 methodA()methodB(),同时选择性的重写了接口 InterfaceA 中的具体函数 methodC()

    五、✔️同一函数继承多个实现的问题

    实现多个接口时,可能会遇到同一函数继承多个实现的问题。例如:

    interface A {
        var name: String
    
        var age: Int
    
        fun foo() {
            println("A")
        }
    
        fun bar()
    }
    
    interface B {
        val age: Int
            get() = 15
    //    val age: String     // 这种情况,实现类C,是无法同时继承接口A和B
    //        get() = "15"
    
        fun foo() {
            println("B")
        }
    
        fun bar() {
            println("bar")
        }
    }
    
    class C : A, B {
        override var age: Int = 0
    
        override var name: String = "C"
    
        override fun foo() {
            super<A>.foo()
            super<B>.foo()
        }
    
        override fun bar() {
            super.bar()
        }
    }
    
    fun main(args: Array<String>) {
        val c =  C()
        println("name = ${c.name}")
        println("age = ${c.age}")
        c.foo();
        c.bar();
    }
    
    2019-06-03 18:32:34.377 24711-24711/cn.ak.kot I/System.out: name = C
    2019-06-03 18:32:34.378 24711-24711/cn.ak.kot I/System.out: age = 0
    2019-06-03 18:32:34.378 24711-24711/cn.ak.kot I/System.out: A
    2019-06-03 18:32:34.378 24711-24711/cn.ak.kot I/System.out: B
    2019-06-03 18:32:34.378 24711-24711/cn.ak.kot I/System.out: bar
    

    当遇到同一函数继承多个实现时,子类必须重写该函数,指定具体调用哪方实现,调用方式super<类名>.函数名(),或都不调用实现子类自己的函数体。属性也是如此,但如果遇到属性名一致但类型不一致时,实现类是无法同时继承双方接口的,如上面被注释代码val age: String

    interface A {
        fun foo()
    }
    
    interface B {
        fun foo(): String
    }
    
    class C : A, B { // 编译错误,无法同时继承接口A和B
        override fun foo(): String {
            return ""
        }
    
        override fun foo() {}
    }
    
    class D : A, B { // 编译错误,无法同时继承接口A和B
        override fun foo(): String {
            return ""
        }
    }
    

    这种情况,接口中抽象函数名一致,返回类型不同,也会导致无法同时继承不同接口。

    相关文章

      网友评论

          本文标题:Kotlin抽象类与接口 (2)✔️接口使用

          本文链接:https://www.haomeiwen.com/subject/atgcxctx.html