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