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关键字进行显式标注。