🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# 类与继承 ## 类 Kotlin 中的类使用 `class` 关键字声明: ``` kotlin class Invoice { } ``` 类由类名、类头(指定它的类型参数、主构造器等等)和类体组成,用花括号包围。类头和类体都是可选的;如果类没有类体则花括号可以省略。 ``` kotlin class Empty ``` ### 构造器 Kotlin 中一个类可以有一个**主构造器**和一个或多个**辅构造器**。主构造器是类头的一部分:它在类名后面(类参数是可选的)。 ``` kotlin class Person constructor(firstName: String) { } ``` 如果主构造器没有注解或可见性修饰符,则 `constructor` 关键字可以省略: ``` kotlin class Person(firstName: String) { } ``` 主构造器不能包含任何代码。初始化代码可以放在有*init*关键字前缀的**初始化块**中: ``` kotlin class Customer(name: String) { init { logger.info("Customer initialized with value ${name}") } } ``` 注意主构造器的参数可以在初始化块中使用。它们也能在类体的属性初始化声明中使用: ``` kotlin class Customer(name: String) { val customerKey = name.toUpperCase() } ``` 事实上,声明属性并在构造器初始化它们,Kotlin 有一个简洁的语法: ``` kotlin class Person(val firstName: String, val lastName: String, var age: Int) { // ... } ``` 常规属性和许多相同的方式一样,属性在主构造器中声明可以是可变的(`var`) 或只读的(`val`)。 如果构造器有注解或可见性修饰符,那么 `constructor` 关键字是必须的,并且这个修饰符在它前面: ``` kotlin class Customer public @Inject constructor(name: String) { ... } ``` 更详细的信息查看 [可见性修饰](visibility-modifiers.md). #### 辅构造器 类还可以与 `constructor` 前缀结合声明**辅构造器**: ``` kotlin class Person { constructor(parent: Person) { parent.children.add(this) } } ``` 如果该类有一个主构造器,每个辅构造器需要委托给主构造器, ``` kotlin class Person(val name: String) { constructor(name: String, parent: Person) : this(name) { parent.children.add(this) } } ``` 如果一个非抽象类没有声明构造器(主或辅),它将生成一个无参主构造器。该构造器的可见性将为公开。如果你不想你的类有一个公开的构造器,你需要声明一个空的非默认可见的主构造器: ``` kotlin class DontCreateMe private constructor () { } ``` > [info]注意:在JVM上,如果构造器的所有参数都有默认值,则编译器将生成一个附加的无参构造器并用默认值初始化。这让 Kotlin 更易于使用一些库,像 Jackson 或 JPA 通过无参构造器创建类实例。 > > ``` kotlin > class Customer(val customerName: String = "") > ``` ### 创建类实例 要创建一个类的实例,我们只要像普通函数一个调用构造器: ``` kotlin val invoice = Invoice() val customer = Customer("Joe Smith") ``` 注意 Kotlin 没有 `new` 关键字。 ### 类成员 类可以包含 * 构造器和初始化块 * [函数](../functions-and-lambdas/functions.md) * [属性](properties-and-fields.md) * [嵌套和内部类](nested-classes.md) * [对象声明](object-declarations.md) ## 继承 Kotlin 中所有的类都有一个公共的超类 `Any`,这是一个默认的超类,不用超类声明: ``` kotlin class Example // 隐含的从 Any 继承 ``` `Any` 不是 `java.lang.Object`;特别要指出的是,它除了 `equals()`、`hashCode()` 和 `toString()` 之外再没有其它的成员。更多信息请查阅[Java interoperability](java-interop.html#object-methods) 章节。 要明确地声明的一个超类,我们在类头的冒号后面放置类型: ``` kotlin open class Base(p: Int) class Derived(p: Int) : Base(p) ``` 如果类有主构造器,则基类可以(并且必须)使用主构造器的参数初始化。 如果类没有主构造器,那么每个辅构造器都得使用 `super` 关键字初始化基类,或是委托给其它构造器来做。注意这种情况下不同的辅构造器可以调用不同的基类构造器: ``` kotlin class MyView : View { constructor(ctx: Context) : super(ctx) { } constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs) { } } ``` 类上的 `open` 注解与 Java 的 `final` 相反:它允许别的类从这个类继承。默认情况下,Kotlin 中所有的类都是 final,类似[Effective Java](http://www.oracle.com/technetwork/java/effectivejava-136174.html)的 Item 17: *Design and document for inheritance or else prohibit it* ### 覆盖方法 正如我们之前所说,在 Kotlin 中我们坚持明确地制造东西。所以与 Java 不同,Kotlin 必需为可覆盖的成员作出明确的注解(我们叫做 `open`)才能被覆盖: ``` kotlin open class Base { open fun v() {} fun nv() {} } class Derived() : Base() { override fun v() {} } ``` 对于 `Derived.v()` 来说 `override` 注解是必需的。如果未找到它,编译器就会提出抗议。如果函数上没有 `open` 注解,比如 `Base.nv()`,在子类中声明一个相同签名的方法是非法的,或者说不管有没有 `override`,在 final 类(例如一个没有 `open` 注解的类)中开放成员都是禁止的。 成员被标记为 `overridde` 表示自身开放,意即它可以被子类覆盖。如果你想要禁止再次被覆盖,则使用 `final` 关键字: ``` kotlin open class AnotherDerived() : Base() { final override fun v() {} } ``` ### 覆盖属性 覆盖属性与覆盖方法类似。超类上的属性声明在衍生类中重复声明必须以 `override` 开头,并且它们必须具有一致的类型。每个声明的属性都能被另一个初始化程序或带有 getter 方法的属性覆盖。 ``` kotlin open class Foo { open val x: Int get { ... } } class Bar1 : Foo() { override val x: Int = ... } ``` 你可以用一个 `var` 覆盖一个 `val`,但反过来则不行。这样的原因是一个 `val` 属性本质上声明了一个 getter 方法,而覆盖成 `var` 则是在衍生类中另外声明一个 setter 方法。 注意你可以在主构造器中的属性声明部分使用 `override` 关键词。 ``` kotlin interface Foo { val count: Int } class Bar1(override val count: Int) : Foo class Bar2 : Foo { override var count: Int = 0 } ``` ### 覆盖规则 在 Kotlin 中,继承的实现通过下面的规则控制:如果一个类从它隐含超类中继承相同成员的一些实现,它必须覆盖这个成员并且提供它自己的实现(也许使用某个已继承的)。要在所继承的实现里表示这个超类,我们合法地使用 `super` 和在尖括号内的超类名,例如 `super<Base>`: ``` kotlin open class A { open fun f() { print("A") } fun a() { print("a") } } interface B { fun f() { print("B") } // interface members are 'open' by default fun b() { print("b") } } class C() : A(), B { // The compiler requires f() to be overridden: override fun f() { super<A>.f() // call to A.f() super<B>.f() // call to B.f() } } ``` 这样很好地从 `A` 和 `B` 继承,而且我们在 `C` 仅仅继承每个函数的一个实现时也不会在 `a()` 和 `b()` 间出现问题。但是对于 `f()` 我们有两个实现通过 `C` 继承,因此我们不得不在 `C` 中覆盖 `f()` 并提供我们自己的实现来排除岐义。 ## 抽象类 类和它的一些成员可以声明为 `abstract`。一个抽象成员在类中没有实现。注意我们不需要注释抽象类或者函数为 open ————它不需要说出来。 我们可以把一个 open 成员覆盖成一个抽象的 ``` kotlin open class Base { open fun f() {} } abstract class Derived : Base() { override abstract fun f() } ``` ## 友元对象 Kotlin 里与 Java 或 C# 不一样的是,类没有静态方法。在大多数情况下,推荐简单地使用包级函数替代。 如果你需要写一个函数让其可以不用类实例就能调用但又要访问这个类的内部(举例:一个工厂方法),你可以把它写成一个类内部的[对象声明](object-declarations.md)成员 更确切地说,如果你在你的类内部声明一个[友元对象](object-declarations.md#companion-objects),你就可以用和 Java/C# 中调用静态方法一样的语法调用其成员而只需要一个合法的类名。