Kotlin 中的继承模式与 Java 有一些不一样之处,主要在三个地方:java
Kotlin 中全部类的最终父类是 Any,而非 Java 中的 Object;app
Kotlin 中 非抽象类默认不可继承;ide
Kotlin 中 非抽象类函数和类属性默认不可覆盖。函数
open 关键字在 Kotlin 中能够用在定义 非抽象的类、类函数和类属性 以前,用来将它们标记为可继承 的:spa
open class Person(val name: String) { open var age = 0 open fun say() = "My name is $name, $age years old."}
override 关键字用在定义 子类覆盖父类函数和属性 以前,用来标记覆盖了父类的函数和属性:orm
class Student(name: String) : Person(name) { override var age = 0 override fun say() = "I'm a student named $name, $age years old."}
须要注意的是,若是类不是 open 的,那么它内部的属性和函数都不能是 open 的。理由很简单,不会被继承的类,不可能有属性和函数被覆盖。对象
虽然 Kotlin 默认没有 open 的属性和函数不能被覆盖,但实际上使用 override 标记和属性和函数能够被覆盖,若是一个 open 类不想让子类覆盖本身 override 的属性和函数,可使用 final override 来覆盖,这样就能够避免再次被子类覆盖了。继承
Kotlin 中没有 extends 关键字,声明一个类继承自另外一个标记为 open 的类的方法是:ci
class 子类[(主构造方法参数)]: 父类[(主构造方法参数)] {……}
能够这样理解,冒号 : 在 Kotlin 中表示前者属于后者类型,好比咱们定义变量时用冒号声明变量的类型,定义函数时用冒号声明函数的返回类型。而在继承中,子类实际上就属于父类的类型,因此没有必要再专门用一个 extends 关键字声明父类,只须要用冒号。it
Java 中,子类在调用自身的构造方法前,会先调用父类的构造方法,因此若是父类有自定义的构造方法,子类就必须显式地定义构造方法,并在构造方法中显式调用父类的构造方法。Kotlin 也是如此,若是父类定义了主构造函数,子类就必须显式地调用父类的主构造函数,缘由与 Java 同样。
因此上面 Student 类在定义时,不能这样写:
// 编译错误,Person 类有自定义的主构造函数class Student(name: String): Person {……}
在覆盖属性时,有两点须要特别注意:
不容许用 val 属性覆盖 var 属性。用 val 属性覆盖 val 属性和用 var 属性覆盖 var 属性很好理解,该是什么样仍是什么样嘛。此外,Kotlin 还容许用 var 属性覆盖 val 属性(只需给子类中的属性添加一个 setter 方法),但不容许用 val 属性覆盖 var 属性:
open class Person() { var name = ""}class Student(): Person(name) { override val name: String = name}
当咱们使用多态时:
val alex: Person = Student()alex.name = "Alex" // 错误,子类对象的属性没有 setter 方法
能够在主构造函数中定义要覆盖的属性:
open class Person(open val name: String)class Student(override val name: String): Person(name)
如上,咱们在 Person 类的主构造函数中将 name 定义为 open 的属性,而后在 Student 类中使用 override 覆盖了它。