继承是面向对象编程语言的一个重要特征。 继承允许将类(或基类或父类)的特性继承到新类(或派生类或子类)。
主类称为超类(或父类),继承超类的类称为子类(或子类)。 子类包含超类的特性以及它自己的特性。
当两个或多个类具有相同的属性时,这就引入继承的概念。 继承用于提高代码可重用性。 派生类只有一个基类,但可以有多个接口,而基类可能有一个或多个派生类。
在Kotlin中,派生类在类头中使用冒号(:
)操作符来继承基类(在派生类名或构造函数之后)。如下代码所示 -
// 声明一个基类 open class Base(p: Int){ } // 声明一个继承自 Base 类(也称为基类)的子类:Derived class Derived(p: Int) : Base(p){ }
假设有两个不同的类:Programmer
和Salesman
,它们具有公共属性name
,age
和salary
以及它们各自的方法:functionalitiesdoProgram()
和fieldWork()
。 继承的特性允许继承包含公共特性的(Employee
基类)。
open class Employee(name: String, age: Int, salary: Float) { // code of employee } class Programmer(name: String, age: Int, salary: Float): Employee(name,age,salary) { // code of programmer } class Salesman(name: String, age: Int, salary: Float): Employee(name,age,salary) { // code of salesman }
所有Kotlin类都有一个共同的超类Any
,它是没有明确指定超类时使用的默认超类。
例如,类Example
是隐式继承Any
类的,即使没有明确写上。
class Example
由于Kotlin类默认为final
,因此不能简单地继承它们。在类之前使用open
关键字来为其它类继承这个类。
示例:
open class Example{ // I can now be extended! }
当继承一个类来派生类时,所有的字段和函数都是继承的。 可以在派生类中使用这些字段和函数。
示例:
open class Base{ val x = 10 } class Derived: Base() { fun foo() { println("x is equal to " + x) } } fun main(args: Array<String>) { val derived = Derived() derived.foo() }
执行上面示例代码,得到以下结果 -
x is equal to 10
open class Bird { fun fly() { println("flying...") } } class Duck: Bird() { fun swim() { println("swimming...") } } fun main(args: Array<String>) { val duck = Duck() duck.fly() duck.swim() }
执行上面示例代码,得到以下结果 -
flying... swimming...
在这里,声明一个类Employee
是超类,Programmer
和Salesman
是它的子类。 子类继承属性:name
, age
和 salary
以及包含子类自身的函数,如:doProgram()
和fieldWork()
。
open class Employee(name: String, age: Int, salary: Float) { init { println("Name is $name.") println("Age is $age") println("Salary is $salary") } } class Programmer(name: String, age: Int, salary: Float):Employee(name,age,salary){ fun doProgram() { println("programming is my passion.") } } class Salesman(name: String, age: Int, salary: Float):Employee(name,age,salary){ fun fieldWork() { println("travelling is my hobby.") } } fun main(args: Array<String>){ val obj1 = Programmer("Maxsu", 25, 40000f) obj1.doProgram() val obj2 = Salesman("Ajax", 24, 30000f) obj2.fieldWork() }
执行上面示例代码,得到以下结果 -
Name is Maxsu. Age is 25 Salary is 40000.0 programming is my passion. Name is Ajax. Age is 24 Salary is 30000.0 travelling is my hobby.
如果基类和派生类都具有主构造函数,则参数在基类的主构造函数中初始化。 在上面的继承示例中,所有类都包含三个参数:name
,age
和salary
,所有这些参数都在基类的主构造函数中初始化。
当基类和派生类在主构造函数中包含不同数量的参数时,基类参数将从派生类对象初始化。
示例代码
open class Employee(name: String,salary: Float) { init { println("姓名:$name.") println("薪水:$salary") } } class Programmer(name: String, dept: String, salary: Float):Employee(name,salary){ init { println("$name 所在部门:$dept ,薪水为:$salary.") } fun doProgram() { println("编程我有激情.") } } class Salesman(name: String, dept: String, salary: Float):Employee(name,salary){ init { println("$name 所在部门:$dept ,薪水为:$salary.") } fun fieldWork() { println("我的爱好是:旅游.") } } fun main(args: Array<String>){ val obj1 = Programmer("Susen", "技术部", 40000f) obj1.doProgram() println() val obj2 = Salesman("Ajax", "市场部", 30000f) obj2.fieldWork() }
执行上面示例代码,得到以下结果 -
姓名:Susen. 薪水:40000.0 Susen 所在部门:技术部 ,薪水为:40000.0. 编程我有激情. 姓名:Ajax. 薪水:30000.0 Ajax 所在部门:市场部 ,薪水为:30000.0. 我的爱好是:旅游.
当创建派生类的对象时,它首先调用超类并执行基类的init
块,然后执行它自己的init
块。
如果派生类不包含任何主构造函数,则需要使用super
关键字从派生类调用基类辅助构造函数。
示例:
open class Patent { constructor(name: String, id: Int) { println("执行超类构造函数: $id , $name ") } } class Child: Patent { constructor(name: String, id: Int, dept: String): super(name, id) { print("使用属性执行子类构造函数:$name, $id, $dept") } } fun main(args: Array<String>) { val child = Child("Maxsu",10010, "技术部") }
执行上面示例代码,得到以下结果 -
执行超类构造函数: 10010 , Maxsu 使用属性执行子类构造函数:Maxsu, 10010, 技术部
在上面的示例中,当创建Child
类的对象时,它调用其构造函数并使用值:“Maxsu”,“10010”和“技术部”初始化其参数。 同时,Child
类构造函数使用具有name
和id
值的super
关键字调用超类的构造函数。 由于super
关键字的存在,超类构造函数的主体首先执行并返回到Child
类构造函数。
方法覆盖意味着将super(parent)
类的方法的特定实现提供到子类(子)类中。
换句话说,当子类重新定义或修改其超类的方法为子类时,它被称为方法覆盖。 方法覆盖只能在继承中实现。
Kotlin方法覆盖的规则
open
的(非final
)。open class Bird { open fun fly() { println("Bird is flying...") } } class Parrot: Bird() { } class Duck: Bird() { } fun main(args: Array<String>) { val p = Parrot() p.fly() val d = Duck() d.fly() }
执行上面示例代码,得到以下结果 -
Bird is flying... Bird is flying...
在上面的例子中,一个没有覆盖基类方法的程序出现派生类Parrot
和Duck
类执行相同的操作。 为了克服这个问题,使用方法覆盖这个概念。
在此示例中,子类Parrot
和Duck
中覆盖父类Bird
的方法fly()
。 要覆盖父类的方法,必须将要覆盖的父类及方法声明为open
。 同时,在子类中重写的方法必须以override
关键字开头。
open class Bird { open fun fly() { println("Bird is flying...") } } class Parrot: Bird() { override fun fly() { println("Parrot is flying...") } } class Duck: Bird() { override fun fly() { println("Duck is flying...") } } fun main(args: Array<String>) { val p = Parrot() p.fly() val d = Duck() d.fly() }
执行上面示例代码,得到以下结果 -
Parrot is flying... Duck is flying...
超类的属性也可以在子类中覆盖,这个实现类似于方法。 在子类Parrot
和Duck
中重写并修改Bird
类的color
属性。
open class Bird { open var color = "黑色" open fun fly() { println("Bird is flying...") } } class Parrot: Bird() { override var color = "绿色" override fun fly() { println("Parrot is flying...") } } class Duck: Bird() { override var color = "花色" override fun fly() { println("Duck is flying...") } } fun main(args: Array<String>) { val p = Parrot() p.fly() println(p.color) val d = Duck() d.fly() println(d.color) }
执行上面示例代码,得到以下结果 -
Parrot is flying... 绿色 Duck is flying... 花色
可以在继承中使用var
属性覆盖val
属性,但反之亦然。
派生类也可以使用super
关键字调用超类方法和属性。
例如:
open class Bird { open var color = "黑色" open fun fly() { println("Bird is flying...") } } class Parrot: Bird() { override var color = "绿色" override fun fly() { super.fly() println("Parrot is flying...") } } fun main(args: Array<String>) { val p = Parrot() p.fly() println(p.color) }
执行上面示例代码,得到以下结果 -
Bird is flying... Parrot is flying... 绿色
在Kotlin中,派生类在尖括号中使用超类型名称,即,super <Base>
,当它实现多个类中提供的相同函数名时。
例如,派生类Parrot
扩展超类Bird
并实现相同的Duck接口函数fly()
。 要调用每个类和接口的特定方法,必须在尖括号中提到超类型名称为super <Bird>.fly()
和super <Duck>.fly()
。
open class Bird { open var color = "黑色" open fun fly() { println("Bird is flying...") } } interface Duck { fun fly() { println("Duck is flying...") } } class Parrot: Bird(),Duck { override var color = "绿色" override fun fly() { super<Bird>.fly() super<Duck>.fly() println("Parrot is flying...") } } fun main(args: Array<String>) { val p = Parrot() p.fly() println(p.color) }
执行上面示例代码,得到以下结果 -
Bird is flying... Duck is flying... Parrot is flying... 绿色