Kotlin中的類是使用class關(guān)鍵字來聲明:
class Invoice {
}
類聲明由類名,類頭(指定類型參數(shù),主構(gòu)造函數(shù)等)和類體組成,由大括號括起來。類頭和類主體都是可選的; 如果類沒有主體,可以省略花括號。如下 -
class Empty
Kotlin中的類可以有一個主構(gòu)造函數(shù)和一個或多個輔助構(gòu)造函數(shù)。 主構(gòu)造函數(shù)是類頭的一部分:它在類名后面(和可選的類型參數(shù))。
class Person constructor(firstName: String) {
}
如果主構(gòu)造函數(shù)沒有任何注釋或可見性修飾符,那么可以省略constructor關(guān)鍵字:
class Person(firstName: String) {
}
主構(gòu)造函數(shù)不能包含任何代碼。 初始化代碼可以放在初始化程序塊中,前綴為init關(guān)鍵字:
class Customer(name: String) {
init {
logger.info("Customer initialized with value ${name}")
}
}
請注意,初始化程序塊中可以使用主構(gòu)造函數(shù)的參數(shù)。 它們也可以用在類體中聲明屬性的初始化器:
class Customer(name: String) {
val customerKey = name.toUpperCase()
}
實際上,要聲明屬性并從主構(gòu)造函數(shù)初始化它們,Kotlin有一個簡潔的語法:
class Person(val firstName: String, val lastName: String, var age: Int) {
// ...
}
與常規(guī)屬性大體相同,主構(gòu)造函數(shù)中聲明的屬性可以是多值(var)或只讀(val)。
如果構(gòu)造函數(shù)具有注釋或可見性修飾符,則constructor關(guān)鍵字是必需的,修飾符將在它之前:
class Customer public @Inject constructor(name: String) { ... }
有關(guān)更多詳細信息,請參閱可見性修飾符。
類還可以聲明輔助構(gòu)造函數(shù),它們以constructor關(guān)鍵字作為前綴:
class Person {
constructor(parent: Person) {
parent.children.add(this)
}
}
如果類具有主構(gòu)造函數(shù),則每個輔助構(gòu)造函數(shù)需要通過另一個輔助構(gòu)造函數(shù)直接或間接地委派給主構(gòu)造函數(shù)。 使用this關(guān)鍵字對同一類的另一個構(gòu)造函數(shù)進行委派:
class Person(val name: String) {
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}
如果一個非抽象類沒有聲明任何構(gòu)造函數(shù)(主或輔助),那么它將不使用參數(shù)來生成主構(gòu)造函數(shù)。 構(gòu)造函數(shù)的可見性將是公開的。 如果不希望類具有公共構(gòu)造函數(shù),則需要聲明具有非默認可見性的空主構(gòu)造函數(shù):
class DontCreateMe private constructor () {
}
注意:在JVM上,如果主構(gòu)造函數(shù)的所有參數(shù)都具有默認值,編譯器將生成一個額外的無參數(shù)構(gòu)造函數(shù),它將使用默認值。 這使得更容易使用Kotlin與諸如Jackson或JPA的庫,通過無參數(shù)構(gòu)造函數(shù)創(chuàng)建類實例。
class Customer(val customerName: String = "")
要創(chuàng)建一個類的實例,需要調(diào)用類的構(gòu)造函數(shù),就像它是一個常規(guī)函數(shù)一樣:
val invoice = Invoice()
val customer = Customer("Joe Minsu")
請注意,Kotlin創(chuàng)建對象并不使用
new關(guān)鍵字。
在嵌套類中描述了創(chuàng)建嵌套,內(nèi)部和匿名內(nèi)部類的實例。
類可以包含 -
Kotlin中的所有類都有一個通用的超類:Any,這是一個沒有父類型的類的默認超類。
class Example // Implicitly inherits from Any
Any不是java.lang.Object; 特別地要說明的是,除了equals(),hashCode()和toString()之外,它不具有其它任何成員函數(shù)。有關(guān)更多詳細信息,請參閱Java互操作性部分。
要聲明一個顯式的超類型,將冒號后面的類型放在類頭中:
open class Base(p: Int)
class Derived(p: Int) : Base(p)
如果類具有主構(gòu)造函數(shù),則可以使用主構(gòu)造函數(shù)的參數(shù)(并且必須)初始化基類型。
如果類沒有主構(gòu)造函數(shù),則每個輔助構(gòu)造函數(shù)必須使用super關(guān)鍵字初始化基類型,或者委托給另一個構(gòu)造函數(shù)。 請注意,在這種情況下,不同的輔助構(gòu)造函數(shù)可以調(diào)用基類型的不同構(gòu)造函數(shù):
class MyView : View {
constructor(ctx: Context) : super(ctx)
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}
一個類的開放(open)注釋與Java的最終結(jié)果相反:它允許其他人繼承這個類。 默認情況下,Kotlin中的所有類都是final,它對應(yīng)于有效Java用法,設(shè)計和繼承的文檔或者禁止它。
正如前面提到的,與Java不同的是,Kotlin需要對可覆蓋成員進行顯式注釋(稱之為open)和覆蓋:
open class Base {
open fun v() {}
fun nv() {}
}
class Derived() : Base() {
override fun v() {}
}
Derived.v()需要覆蓋(override)注釋。 如果缺少(override)注釋,編譯器會抱錯。 如果在一個函數(shù)上沒有open注釋,如在Base.nv()中,在子類中聲明一個具有相同簽名的方法是非法的,無論是否有覆蓋(override)注釋還是沒有。 在final類(例如不使用open注釋的類)中,則禁止覆蓋成員。
標(biāo)記為覆蓋(override)的成員本身是打開的,即它可以在子類中被覆蓋。 如果要禁止重新覆蓋,請使用final關(guān)鍵字:
open class AnotherDerived() : Base() {
final override fun v() {}
}
覆蓋屬性的工作方式與覆蓋方法類似; 在超類上聲明,然后在派生類上重新聲明的屬性必須以override替代,并且它們必須具有兼容類型。 每個聲明的屬性可以被具有初始化器的屬性或具有getter方法的屬性覆蓋。
open class Foo {
open val x: Int get { ... }
}
class Bar1 : Foo() {
override val x: Int = ...
}
還可以使用var屬性覆蓋val屬性,反之亦然。 這是允許的,因為val屬性基本上聲明一個getter方法,并將其替換為var,另外在派生類中聲明一個setter方法。
請注意,可以在主構(gòu)造函數(shù)中使用override關(guān)鍵字作為屬性聲明的一部分。
interface Foo {
val count: Int
}
class Bar1(override val count: Int) : Foo
class Bar2 : Foo {
override var count: Int = 0
}
在Kotlin中,實現(xiàn)繼承由以下規(guī)則控制:如果類從其直接超類繼承同一成員的多個實現(xiàn),則它必須覆蓋該成員并提供自己的實現(xiàn)(可能使用其中一個繼承)。 要表示從其繼承的實現(xiàn)的超類型,可在尖括號中使用超類型名稱超級限定,例如,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()
}
}
從B繼承A是沒有問題的,對a()和b()函數(shù)也沒有任何問題,因為C只繼承每個這些函數(shù)的一個實現(xiàn)。 但是對于f()有兩個由C繼承的實現(xiàn),因此必須在C中重寫f()函數(shù)并提供自己的消除歧義的實現(xiàn)。
一個類和其一些成員可以被聲明為抽象。 抽象成員在其類中沒有實現(xiàn)。 請注意,不需要使用open來注釋抽象類或函數(shù)。
可以用抽象來覆蓋一個非抽象的open成員 -
open class Base {
open fun f() {}
}
abstract class Derived : Base() {
override abstract fun f()
}
在Kotlin中,與Java或C#不同,類沒有靜態(tài)(static)方法。 在大多數(shù)情況下,建議簡單地使用包級別的功能。
如果需要編寫一個可以調(diào)用的函數(shù),而不需要一個類實例,但需要訪問一個類的內(nèi)部(例如,一個工廠方法),則可以將其作為對象聲明的一個成員編寫。
更具體地說,如果在類中聲明了一個伴隨對象,則可以使用類名作為限定符的Java/C#中調(diào)用靜態(tài)方法相同的語法來調(diào)用其成員。