在Kotlin中所有类都有一个共同的父类Any
,这对于没有父类声明的类是默认父类。这跟Java是类似的,只不过在Java中默认父类是Object
。
这里我们看看Any
类源码:
public open class Any {
/**
* Indicates whether some other object is "equal to" this one. Implementations must fulfil the following
* requirements:
*
* * Reflexive: for any non-null value `x`, `x.equals(x)` should return true.
* * Symmetric: for any non-null values `x` and `y`, `x.equals(y)` should return true if and only if `y.equals(x)` returns true.
* * Transitive: for any non-null values `x`, `y`, and `z`, if `x.equals(y)` returns true and `y.equals(z)` returns true, then `x.equals(z)` should return true.
* * Consistent: for any non-null values `x` and `y`, multiple invocations of `x.equals(y)` consistently return true or consistently return false, provided no information used in `equals` comparisons on the objects is modified.
* * Never equal to null: for any non-null value `x`, `x.equals(null)` should return false.
*
* Read more about [equality](https://kotlinlang.org/docs/reference/equality.html) in Kotlin.
*/
public open operator fun equals(other: Any?): Boolean
/**
* Returns a hash code value for the object. The general contract of `hashCode` is:
*
* * Whenever it is invoked on the same object more than once, the `hashCode` method must consistently return the same integer, provided no information used in `equals` comparisons on the object is modified.
* * If two objects are equal according to the `equals()` method, then calling the `hashCode` method on each of the two objects must produce the same integer result.
*/
public open fun hashCode(): Int
/**
* Returns a string representation of the object.
*/
public open fun toString(): String
}
由上可知,也就为所有kotlin类都定义了这些方法。
Kotlin类默认是最终(final
)的:不能被继承。如果需要被继承,请用关键字open
:
class Shape() //不可被继承(默认)
open class Shape() //可被继承
如需继承父类,请在类头中把父类放到冒号之后:
open class Circle: Shape()
如果子类有一个主构造函数,其父类必须用子类主构造函数的参数“就地”初始化,有点抽象,看代码就明白了:
//父类
open class Shape(name:String){}
//子类, “就地”初始化
open class Circle(name:String): Shape(name){}
如果子类没有主构造函数呢?那就用带参数的次构造函数,父类总得初始化吧:
open class Circle: Shape {
constructor(name: String) : super(name) {
}
//委托给上面的构造函数
constructor(name: String, color: String) : this(name) {
}
}
如上,每个次构造函数必须用关键字super
初始化父类,或委托给另外一个构造函数做到这一点。 当然,不同的次构造函数具体委托父类哪个构造方法,就看我们自己决定了:
open class Shape(name:String) {
constructor(name:String, color: String) : this(name) {
}
}
//子类
open class Circle: Shape {
constructor(name: String) : super(name) {
}
//这里我也可以委托父类主构造函数 super(name),但是这样color参数就没意义了
constructor(name: String, color: String) : super(name, color) {
}
}
覆盖方法
一个方法如果想被子类覆盖,仍然需要使用关键字open
:
open class Shape(name:String) {
constructor(name:String, color: String) : this(name) {
}
//可被子类覆盖
open fun name() {
print("fun##name.")
}
//子类不可覆盖
fun draw(){}
}
//子类
open class Circle: Shape {
...
override fun name() {
super.name()
}
...
}
如上,name()
函数被覆盖,同时它也可以继续被Circle
的子类覆盖(相当于加了open
)。如果想禁止被子类覆盖,可以使用final
关键字。
...
final override fun name() {
super.name()
}
...
覆盖属性
属性覆盖跟方法覆盖类似,均需要使用open
关键字修饰。
open class Shape(name:String) {
//属性
open val tempAttribute : String = ""
constructor(name:String, color: String) : this(name) {
}
open fun name() {
print("fun##name.")
}
fun draw(){}
}
//子类
open class Circle: Shape {
//覆盖属性
override val tempAttribute: String = "Circle"
constructor(name: String) : super(name) {
}
//我也可以委托父类主构造函数 super(name), 和上面的函数一样
constructor(name: String, color: String) : super(name, color) {
}
final override fun name() {
super.name()
}
}
//子类覆盖属性还有种简单的写法,直接在主构造函数进行覆盖
open class Circle(name:String, override val tempAttribute: String): Shape(name) {
final override fun name() {
super.name()
}
}
这里需要备注说明一下,我们可以使用var
属性覆盖一个val
属性,但反之不行。 你如果强行这么做,IDE也不允许,直接报错:
调用父类的方法
子类调用父类的函数或者属性访问器的实现可以使用super
关键字:
open class Circle(name:String): Shape(name) {
val childTempAttr: String = super.tempAttribute
override fun name() {
super.name()
}
}
有两种使用场景需要注意下:
- 场景一: 在内部类访问外部类的父类成员方法,可以通过由外部类名限定的
super
关键字来实现:
open class Circle(name:String): Shape(name) {
val childTempAttr: String = super.tempAttribute
override fun name() {
super.name()
}
//内部类
inner class Ball {
fun draw() {
//直接调用外部类的方法
name()
//调用外部类父类的方法
super@Circle.name();
}
}
}
挺怪异的写法,我们把代码转成Java看看:
...
public final class Ball {
public final void draw() {
Circle.this.name();
Circle.super.name();
}
}
...
是不是有种“拨开云雾见天日”的感觉…
- 场景二: 一个类继承了一个类同时实现了一个接口,并且两个父类都有相同的方法,子类如何调用父类的这个方法,如何区分呢?
//父类1
open class Rectangle {
open fun draw() {
}
}
//父类2
interface Polygon {
fun draw(){
}
}
//子类
class Square : Rectangle(), Polygon {
override fun draw() {
//调用Rectangle
super<Rectangle>.draw()
//调用Polygon
super<Polygon>.draw()
//子类也可以不调用父类,自己实现自己的逻辑
/** todo */
}
}
子类初始化顺序
如下代码,打印的结果是啥?
//父类
open class Shape(name:String) {
open val tempAttribute : String = "Shape##tempAttribute".also { println(it) }
init {
println("Shape##init.")
}
constructor(name:String, color: String) : this(name) {
println("Shape##constructor")
}
}
//子类
class Square : Shape {
override val tempAttribute: String = "Square##tempAttribute".also { println(it) }
init {
println("Square##init")
}
constructor(name: String, color: String) : super(name, color) {
println("Square##constructor")
}
}
//执行
fun main(args: Array<String>) {
Square("正方形","红色")
}
//打印结果
Shape##tempAttribute
Shape##init.
Shape##constructor
Square##tempAttribute
Square##init
Square##constructor
所以父类的初始化是最先执行的,接着才是子类的初始化,跟Java别无二致。
上一篇:Kotlin学习历程——类的定义。
下一篇:Kotlin学习历程——接口