继承
第一点: 在kotlin中声明一个类, 它默认是Any这个类的子类, 就和java中一个类默认是Object这个类的子类一样, 看下面类的声明:
// 默认是Any的子类, 类似于java里所有的类都是Object的子类一样
class CDemo
我们来看一下Any类是怎么定义的
这里需要注意几点:
1.Any类声明的时候前面有个open, 注意kotlin中如果类前面不加open, 则该类默认是final的, 也就是不能被继承, 当尝试去继承的时候会有如下错误:
Error:(8, 15) Kotlin: This type is final, so it cannot be inherited from
例如:
// 默认是Any的子类, 类似于java里所有的类都是Object的子类一样
class CDemo : Any()
// 如果前面不加 open 关键字, 则默认的类是final的, 也就是不能被集成, 会提示如下错误
// Error:(8, 15) Kotlin: This type is final, so it cannot be inherited from
class Demo2 : CDemo() {
}
2.Any类默认实现了三个方法: equal, hashcode, toString
第二点: 在写子类的时候需要注意的是: 如果子类有主构造函数, 那么基类必须在主构造函数中立即初始化, 例如:
open class Animal
// 如果子类有主构造函数, 那么基类必须在主构造函数中立即初始化
// 继承 使用":" 表明继承自哪个类, 这里Animal要写() 的原因是父类没有显式的构造函数, 所以系统加了一个默认的空参的构造函数
// 而如果父类有构造函数那么子类必须实现父类的构造函数
open class Dog(type : String) : Animal() {
}
Dog继承自Animal, 但是由于Animal里有个默认的构造函数, 所以需要 写成 ": Animal()", 如果父类有带参数的主构造函数, 那么一旦子类也有主构造函数一样也需要去实现父类的主构造函数例如:
// 由于Dog有一个带参的主构造函数, 所以ErHa在继承的时候必须去实现这个构造函数
class Husky(type : String, name : String) : Dog(type) {
}
第三点: 如果子类没有主构造函数, 那么必须要在次构造函数中用 super 关键字初始化父类,或者调用另一个次构造函数。初始化父类时,可以调用父类的不同构造方法。例如:
// 如果子类有主构造函数, 那么基类必须在主构造函数中立即初始化
// 继承 使用":" 表明继承自哪个类, 这里Animal要写() 的原因是父类没有显式的构造函数, 所以系统加了一个默认的空参的构造函数
// 而如果父类有构造函数那么子类必须实现父类的构造函数
open class Dog(type : String) : Animal() {
}
// 由于Dog有一个带参的主构造函数, 所以ErHa在继承的时候必须去实现这个构造函数
class Husky(type : String, name : String) : Dog(type) {
}
class GoldenRetriever : Dog {
// 如果子类没有主构造函数, 有次构造函数, 需要在次构造函数中用super关键字调用父类的构造方法
constructor(type : String = "金毛" , name: String, age: Int) : super (type) {
}
}
class Alaska : Dog {
// 如果子类没有主构造函数, 有次构造函数, 需要在次构造函数中用super关键字调用父类的构造方法
constructor(name : String, age : Int) : super(name) {
}
// 也可以在次构造函数中, 调用其他的次构造函数, 再由其调用父类的构造函数
constructor(type: String = "阿拉斯基", name: String, age: Int) : this (name, age){
}
}
重写
需要注意的是如果父类的方法没有open声明, 那么该方法是不允许被重写的, 重写方法的时候需要使用override关键字注明, 还有一点就是子类可以选择不重写父类的方法, 示例代码如下:
package com.wbing.kotlindemo.demo2
open class Dog {
fun shout() {
println("汪汪")
}
open fun show() {
println("萌萌哒")
}
}
class Husky : Dog() {
// 由于父类的shout方法没有写open, 也就是默认是final的, 所以这里不能重写shout方法
// Error:(15, 5) Kotlin: 'shout' in 'Dog' is final and cannot be overridden 不能重写shout因为默认是final的
/* override fun shout() {
println("玩明汪")
}*/
// 重写父类方法的时候要加上override关键字, show方法也可以选择不进行重写
override fun show() {
println("傻")
}
}
fun main(args: Array<String>) {
val husky = Husky()
husky.shout()
husky.show()
}
再次需要注意的是, 如果一个类集成了多个类/接口, 并且父类/接口中有相同名字的方法需要重写的时候, 那么子类这时候必须重写该方法, 并且如果子类想区分开父类的方法的时候, 可以使用super关键字调用不同父类的方法, 示例代码如下:
package com.wbing.kotlindemo.demo2
open class Dog {
fun shout() {
println("汪汪")
}
open fun show() {
println("萌萌哒")
}
}
interface WatchDog {
// interface里不用写open, 因为默认就是open的
fun show() {
println("看门电子狗")
}
}
// 使用接口的时候不用写(), 是因为接口是没有构造函数的
class Husky : Dog(), WatchDog {
// 由于父类的shout方法没有写open, 也就是默认是final的, 所以这里不能重写父类的方法
// Error:(15, 5) Kotlin: 'shout' in 'Dog' is final and cannot be overridden 不能重写shout因为默认是final的
/*override fun shout() {
println("玩明汪")
}*/
// 重写父类方法的时候要加上override关键字
override fun show() {
// 当重写的方法在父类中有多个实现的时候, 如果继承的多个类中, 有相同的方法需要重写的时候, 则子类必须重写该方法, 并且, 为了区分, 可以使用super关键字来调用不同的父类中的方法
super<WatchDog>.show() // 看萌电子狗
super<Dog>.show() // 萌萌哒
}
}
fun main(args: Array<String>) {
val husky = Husky()
husky.shout() // 汪汪
husky.show()
}
关键需要注意的地方, 注释写的比较明白
重写父类的变量:
父类变量的重写的时候有几个需要注意的地方, 被重写的变量也要有open的声明, 并且, 子类可以使用var类型的变量去重写父类val类型的变量, 但是不能使用val类型的变量去重写父类var类型的变量, 如果使用val类型的变量去重写父类的var类型的变量, 那么子类这个val类型的变量会多一个set方法, 而val类型的变量是不允许有set方法的
package com.wbing.kotlindemo
open class A {
open val x: Int get() {
return 0
}
open var y : String = "a"
}
class B : A() {
override val x: Int = 1
}
class C : A() {
override var x : Int = 2
// Error:(17, 14) Kotlin: Var-property public open val y: String defined in com.wbing.kotlindemo.C cannot be overridden by val-property public open var y: String defined in com.wbing.kotlindemo.A
override val y : String = "b"
}