继承
Kotlin中所有的类都有共同的父类Any ,默认是缺省父类的,例如:
class Gobj // 隐式继承于 Any
Any不是 java.lang.Object;事实上它除了 equals(),hashCode()以及toString()外没有任何成员了。
声明一个明确的父类,需要在类头后加冒号再加父类:
open class Base(p: Int)
class Derived(p: Int) : Base(p)
如果类有主构造器,则基类可以而且是必须在主构造器中立即初始化。
如果类没有主构造器,每一个二级构造器中不得不用 super 关键字初始化基类,或者再代理另一个构造器做这件事。注意在这种情形中不同的二级构造器可以调用基类不同的构造方法:
class MyView : View {
constructor(ctx: Context) : super(ctx) {
}
constructor(ctx: Context, attrs: AttributeSet) : super(ctx,attrs) {
}
}
open 注解与java 中的 final相反:它允许别的类继承这个类。默认情形下,kotlin 中所有的类都是 final ,即不允许继承。
重写方法
像之前提到的,我们在 kotlin 中坚持做明确的事。不像 java ,kotlin 需要把可以重写的成员都明确注解出来,并且重写它们:
open class Base {
open fun v() {}
fun nv() {}
}
class Derived() : Base() {
override fun v() {}
}
对于 Derived.v() 来说 override 注解是必须的。如果没有加的话,编译器会提示。如果没有 open 注解,像 Base.nv() ,在子类中声明一个同样的函数是不合法的,要么加 override 要么不要复写。在 final 类(就是没有open注解的类)中,open 类的成员是禁止的。
标记为 override 的成员是 open的,它可以在子类中被复写。如果你不想被重写就要加 final。
重写属性
重写属性和重写方法一样,父类的属性在子类被重写的时候,必须指定override,并且,必须是兼容的类型。每一个属性都能通过初始化或者getter方法重写。
open class Foo {
open val x: Int get { ... }
}
class Bar1 : Foo() {
override val x: Int = ...
}
你也能够用var属性重写一个val属性,但是不是基于变量。允许这样是因为val属性建了一个一个getter方法,并且重写为一个var属性是额外的声明了一个setter方法。
【注意】:你可以在主构造器中用override关键字声明一部分属性。
interface Foo {
val count: Int
}
class Bar1(override val count: Int) : Foo
class Bar2 : Foo {
override var count: Int = 0
}
重写规则
在 kotlin 中,实现继承通常遵循如下规则:如果一个类从它的直接父类继承了同一个成员的多个实现,那么它必须复写这个成员并且提供自己的实现(或许只是直接用了继承来的实现)。为表示使用父类中提供的方法,我们用 super<Base>表示:
open class A {
open fun f () { print("A") }
fun a() { print("a") }
}
interface B {
fun f() { print("B") } //接口的成员变量默认是 open 的
fun b() { print("b") }
}
class C() : A() , B{
override fun f() {
super<A>.f()//调用 A.f()
super<B>.f()//调用 B.f()
}
}
可以同时从 A B 中继承方法,而且 C 继承 a() 或 b() 的实现没有任何问题,因为它们都只有一个实现。但是 f() 有俩个实现,因此我们在 C 中必须复写 f() 并且提供自己的实现来消除歧义。
抽象类
一个类或一些成员可能被声明成 abstract 。一个抽象方法在它的类中没有实现方法。记住我们不用给一个抽象类或函数添加 open 注解,它默认是带着的。
我们可以用一个抽象成员去复写一个带 open 注解的非抽象方法。
open class Base {
open fun f() {}
}
abstract class Derived : Base() {
override abstract fun f()
}
伴随对象
在 kotlin 中不像 java 或者 C# 它没有静态方法。在大多数情形下,我们建议只用包级别的函数。
如果你要写一个没有实例类就可以调用的方法,但需要访问到类内部(比如说一个工厂方法),你可以把它写成它所在类的一个成员。
更高效的方法是,你可以在你的类中声明一个伴随对象,这样你就可以像 java/c# 那样把它当做静态方法调用,只需要它的类名做一个识别就好了。