1.超类(Any)
在Kotlin中,所有的类都继承于Any类,如果没有显示声明父类就默认继承自Any。
class Person // Person默认继承自Any
Any这个类给我们提供了equals()、hashCode()、toString()这三个方法,
如下:
public open class Any {
public open operator fun equals(other: Any?): Boolean
public open fun hashCode(): Int
public open fun toString(): String
}
2.继承规则
默认情况下,Kotlin 类是final的,表示不能被继承。 要使一个类可继承,请用 open 关键字标记它
final class Person //final是默认的,自动省略,表示该类不能被继承
open class Person // open是需要显示添加的,表示该类可以被继承
Java继承是使用extends关键字,而Kotlin是使用:
符号
示例:
open class Base(p: Int)
class Derived(p: Int) : Base(p)
3. 继承类的构造函数
子类中的代码可以使用 super 关键字调用其父类的函数与属性访问器的实现。
Kotlin类,可以有一个主构造函数,或者多个次构造函数。
1.)当子类无主构造函数时,则每个辅助构造函数必须使用super关键字初始化父类的构造函数,或者委托给另一个构造函数。
示例:这里举例在Android中常见的自定义View实现,我们熟知,当我们指定一个组件是,一般实现父类的三个构造函数。
class MyView : View {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int)
: super(context, attrs, defStyleAttr)
}
2.)如果子类存在主构造函数
当子类存在主构造函数时,主构造函数一般实现父类中参数最多的构造函数,参数少的构造函数则用this关键字引用该构造函数即可
示例如下:
class MyView(context: Context?, attrs: AttributeSet?, defStyleAttr: Int)
: View(context, attrs, defStyleAttr) {
constructor(context: Context?) : this(context,null,0)
constructor(context: Context?,attrs: AttributeSet?) : this(context,attrs,0)
}
3.)子类的初始化顺序
子类初始化之前,会先完成父类的初始化
这意味着,父类构造函数执行时,子类中声明或覆盖的属性都还没有初始化。如果在父类初始化逻辑中(直接或通过另一个覆盖的 open 成员的实现间接)使用了任何一个这种属性,那么都可能导致不正确的行为或运行时故障。设计一个父类时,应该避免在构造函数、属性初始化器以及 init 块中使用 open 成员。
官方示例:
open class Base(val name : String) {
init {
println("Initializing Base")
}
open val size : Int = name.length.also { println("Initializing size in Base: $it") }
}
class Derived(
name : String ,
val lastName : String ,
) : Base(name.capitalize().also { println("Argument for Base: $it") }) {
init {
println("Initializing Derived")
}
override val size : Int = (super.size + lastName.length).also {
println("Initializing size in Derived: $it")
}
}
运行结果是:(also的作用是 对一个对象进行操作并返回该对象)
Argument for Base: Hello
Initializing Base
Initializing size in Base: 5
Initializing Derived
Initializing size in Derived: 10
可以看到 先初始化了父类的初始化代码块和属性,然后再初始化了子类的初始化代码块和属性。
4. 方法的重写和重载
重写(覆盖)父类里的方法,称为重写
父类中的函数没有用open修饰符修饰时,默认是final的,不能被重写
当父类中的函数,没有用open修饰符修饰的时候,子类中出现的函数的函数名不能与父类中没有用open修饰符修饰的函数的函数名相同,不管实现类中的该函数有无override修饰符修饰。
示例:Circle.draw() 函数上必须加上 override 修饰符。如果没写,编译器将会报错。 如果函数没有标注 open 如 Shape.fill(),那么子类中不允许定义相同签名的函数, 不论加不加 override。将 open 修饰符添加到 final 类(即没有 open 的类)的成员上不起作用。
open class Shape {
open fun draw() { /*……*/ }
fun fill() { /*……*/ }
}
class Circle() : Shape() {
override fun draw() { /*……*/ }
}
方法重载
方法的重载。即函数名相同,函数的参数不同的情况。和Java是相同的。
5. 属性的重写
属性也默认是final 修饰的,如果想重写属性,也需要用open 修饰
属性覆盖与方法覆盖类似;在父类中声明,然后在子类中重新声明的属性必须以 override 开头,并且它们必须具有兼容的类型。 每个声明的属性可以由具有初始化器的属性或者具有 get 方法的属性覆盖。
示例:
open class Person {
open val userWork = "无职业"
}
class Teacher : Person() {
override val userWork : String="教师"
}
你也可以用一个 var 属性覆盖一个 val 属性,但反之则不行。 这是允许的,因为一个 val 属性本质上声明了一个 get 方法, 而将其覆盖为 var 只是在子类中额外声明一个 set 方法。
Getter()函数慎用super关键字,super代表父类
重写属性的时候如果用get() = super.xxx,这样的话,不管你是否重新为该属性赋了新值,还是支持setter(),在使用的时候都调用的是基类中的属性值。
如下:
open class Person {
open val userWork = "无职业"
}
class Teacher : Person() {
override var userWork : String="教师"
get() = super.userWork
set(value) {
field = value
}
}
val teacher = Teacher()
Log.e("职业是",teacher.userWork)
上述代码的运行结果是:
职业是: 无职业