Kotlin类和继承
原文地址
Classes
类在Kotlin中使用class关键字被声明
class Invoice {
}
类声明由类名和类头组成(指定它的类型参数,主要构造函数等等)。被花阔号括起来的类体。类头和类体是可选的。如果那个类没有类体,花括号能够被省略。
class Empty
Constructors(构造器)
在Kotlin中一个类能够有一个主要构造函数和一个或多个次级构造函数。首要构造函数是类头的一部分:他在类名后面(可选类型参数)
class Person constructor(firstName: String) {
}
如果主要构造函数没有任何注解和可见修饰符,constructor关键字可以被省略:
class Person(firstName: String) {
}
主要构造函数不能包含任何代码。初始化代码能够放到以init为前缀的初始化器中。
class Customer(name: String) {
init {
logger.info("Customer initialized with value ${name}")
}
}
注意:主要构造函数的参数能够被用于初始化代码块中。他们也能被用于类体中的属性值初始化。
class Customer(name: String) {
val customerKey = name.toUpperCase()
}
事实上,为了声明属性值并且在主要构造函数中初始化,Kotlin有一个简洁的语法:
class Person(val firstName: String, val lastName: String, var age: Int) {
// ...
}
普通的属性值很相似,在主要构造函数中声明的属性值有能够被改变(var)或者只读(val)两种
如果一个构造函数有注解或者可见修饰符,constructor关键字是被需要的,并且修饰符在它之前
class Customer public @Inject constructor(name: String) { ... }
关于更多细节,参看Visibility Modifiers
Secondary Constructors(次级构造器)
类也能被声明为以constructor关键字为前缀的次级构造器。
class Person {
constructor(parent: Person) {
parent.children.add(this)
}
}
如果类有一个主要构造器,每一个次级构造器需要去代理主构造器,直接或者间接的方式通过另一个次级构造器。代理同一类的另一个构造器使用this关键字:
class Person(val name: String) {
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}
如果一个非抽象的类不声明任何构造器(主要的或者次级的),它将生成一个没有参数的构造器。构造器的可见性将是public。如果你不想你的类有一个公共构造器。你需要声明一个不带默认可见性的空的构造器:
class DontCreateMe private constructor () {
}
注意:在JVM中,如果所有主要构造器的参数有默认值,编译器将会生成一个额外的无参使用默认值的构造器。这将使Kotlin使用像Jackson或者JPA创建class实例通过无参构造函数变得容易。
class Customer(val customerName: String = "")
Creating instances of classes(创建类实例)
为了创建一个类的实例,我们调用构造器如果它是一个常规的函数:
val invoice = Invoice()
val customer = Customer("Joe Smith")
注意Kotlin没有new关键字
创建一个嵌套的,内部的,匿名内部类的实例在 Nested classes中有描述
Class Members(类成员)
类能够包含:
- 构造参数和初始化器
- 函数
- 属性值
- 嵌套和内部类
- 对象声明
Inheritance(继承)
所有在Kotlin中的类有一个公共的超类Any。这是一个默认的没有任何超类型被声明的超类。
···
class Example // Implicitly inherits from Any
···
Any
不是java.lang.object
;在特殊情况下,它不包含任何的成员变量除了equals(), hashCode()
和toString()
.更多细节请参考Java interoperability
为了声明一个明确的超类型,我们把类型放置到类标题的分号后:
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’s final的反义词;它允许其他类继承于这个类。默认情况下,在Kotlin中所有的类是final的,这是符合Effective Java, Item 17: Design and document for inheritance or else prohibit it.
Overriding Methods(重写方法)
正如我们之上所提出的,在Kotlin中,我们执着于让事情变的明确,不像Java,Kotlin需要明确的的注解对于可重写成员(我们称之为open)和要重写的。
open class Base {
open fun v() {}
fun nv() {}
}
class Derived() : Base() {
override fun v() {}
}
Derived.v()需要有重写注释,如果它缺失了,编译器将会报警。如果没有open注解在函数上,像Base.nv(),用相同的签名声明方法在子类中是非法的,不管有没有override关键字。在一个final类中(也就是没有open注解的类),open成员是被禁止的。
一个成员被标记为override它本身是open,也就是可能在子类中被重写。如果你想禁止重写,用final:
open class AnotherDerived() : Base() {
final override fun v() {}
}
Overriding Properties(重写属性)
重写属性的工作方式与重写方法相同;在超类中声明的属性在子类中再次声明必须以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
}
Overriding Rules(重写规则)
在Kotlin,继承实现是被如下规则管制:如果一个类从它的直接超类中继承了很多同一成员的实现,它必须重写这个成员并且提供它自己的实现(可能,使用被继承的某一个)。为了表示被继承实现的超类型,我们可以使用超限定名通过尖括号中的超类型名称,也就是super<Base>:
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是ok的,对于a()和b()这里没有问题因为c继承了这些函数的一种实现。但是对于f()我们有两种被c继承的实现,因此我们必须重写在c中重写f()并且提供我们自己的实现去消除歧义。
Abstract Classes(抽象类)
一个类和它的成员可能被声明为abstract。一个抽象的成员在它的类中没有实现。注意我们不需要注解一个抽象类或者函数通过open-它是不需要说明的
我们可以重写一个非抽象open成员通过一个抽象的
open class Base {
open fun f() {}
}
abstract class Derived : Base() {
override abstract fun f()
}
Companion Objects(伴随体)
在Kotlin中,不像Java或者C#,类没有静态成员,多数情况下,建议只使用包级别的函数。
如果需要一个没有类实例但能被访问类内部且能够被调用的函数(例如,一个工厂函数),你可以把它写为一个类中的object declaration 成员
更具体的说,如果你在类中声明一个 companion object,你将能够
调用它的函数使用相同的语法像调用Java/C#中的静态函数那样,仅仅使用类名称当做限定符。