当前位置:嗨网首页>书籍在线阅读

05-继承

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

5.2 继承

继承是面向对象编程的重要概念,即从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为并扩展新的能力。通过继承父类的特征和行为,可以使子类对象(或实例)具有父类的实例域和方法。

1.类的继承

Java使用extends关键字来表明继承关系,Kotlin则使用冒号的方式来表明继承关系。在Kotlin中所有类都有一个共同的超类Any,它是一个默认超类。

class Example     //从Any隐式继承

要声明一个显式的超类型,可以把类型放到类头的冒号之后。代码如下。

open class Base(p: Int)
class Example(p: Int) : Base(p)

如果该类有一个主构造函数,那么其基类型可以用主构造函数的参数进行初始化;如果该类没有主构造函数,那么每个次构造函数必须使用super关键字初始化其基类型,或委托给另一个构造函数初始化。需要注意的是,不同的次构造函数调用基类型的构造函数也不同。

class Example : View {
    constructor(ctx: Context) : super(ctx)
    constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}

对于上例中讲到的open标注,使用open标注的类允许被其他类继承,它的作用和Java的final正好相反。默认情况下,Kotlin中所有的类都是final类型的。

2.方法重写

和Java不一样,Kotlin的方法重写时需要显式地注解和重写操作。代码如下。

open class Base {
    open fun v() {
        println("Base.v()")
}
    fun nv() {
        println("Base.nv")
    }
}
class Derived() : Base() {
    override fun v() {
        println("Derived.v()")
    }
}

在上面的例子中,覆写Derived的v()时,override注解是必需的,否则编译器会报错。对于Base类的nv()来说,其中没有open注解,子类中是不能覆写该方法的。在一个final类中,没有open注解声明,open成员是禁止的,也就是说final类的每个成员也都是final类型的。例如,下面的例子编译会报错。

final class Base { … }
class Derived() : Base() {   编译报错,final类不能被继承
      …   }

对于一个标记为override的成员来说,因为其本身就是open的,所以子类仍然可以覆写它,如果想禁止覆写,那么可以使用final关键字。

open class AnotherDerived() : Base() {
    final override fun v() {}
}

3.属性重写

属性重写和方法重写类似,如果子类要重新声明父类已经声明过的属性,可以使用override关键字来重写相关的属性,在重写属性时应做到类型的兼容。每个声明的属性可以被初始化或被具有get方法的属性所覆盖,属性重写需要使用open关键字修饰。

open class Foo {
    open val x: Int
        get() {
            println("Foo")
            return 3
        }
}
class Example : Foo() {
    override val x: Int = 2
}

Kotlin允许使用var属性覆盖val属性,反之则不可以。这是因为val属性本身就声明了一个get方法,而将其覆盖为var属性时,只是在子类中额外声明一个set方法。

4.调用超类

在派生类中,可以使用super关键字来调用超类的函数与属性。代码如下。

open class Foo {
    open fun f() { println("Foo.f()") }
    open val x: Int get() = 1
}
class Bar : Foo() {
    override fun f() { 
        super.f()
        println("Bar.f()") 
    }
    override val x: Int get() = super.x + 1
}

如果需要在一个内部类中访问外部类的超类,可以使用由super关键字限定的外部类名来实现。代码如下。

open class Foo {
    open fun f() { println("Foo.f()") }
    open val x: Int get() = 1
}
class Bar : Foo() {
    override fun f() { /* …… */ }
    override val x: Int get() = 0
    inner class Baz {
        fun g() {
            [email protected]()            //调用Foo实现的 f()
            println([email protected])     //使用Foo实现的x的getter
        }
    }
}

5.规则覆写

在Kotlin中,实现继承要遵循以下规则控制:如果直接从超类继承同一成员的多个实现,则它必须覆盖该成员并提供自己的实现。如果要表明继承的关系,可在尖括号中使用超类型名称限定符(如super)。

open class A {
    open fun f() { print("A") }
    fun a() { print("a") }
}
interface B {
    fun f() { print("B") }      //接口成员默认就是open的
    fun b() { print("b") }
}
class C() : A(), B {
    //编译器要求覆盖f():
    override fun f() {
        super<A>.f()      //调用A.f()
        super<B>.f()      //调用B.f()
  }
}

在上面的代码中,同时继承A和B是没问题的,但是对于C中的f()来说,因为A和B中都具有f(),所以在覆盖f()时需要提供具体的实现来消除歧义。

到此,再来看一下有关Kotlin继承相关的知识,有以下几点需要注意。

  • Kotlin中的类默认是final类型的,如果想要被子类继承,需要使用open关键字修饰。
  • 在Kotlin中,方法默认是不允许覆写的,只有用open修饰时,子类才可以覆写相关方法,而且需要使用override关键字进行显式标注。