<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # 類與繼承 [TOC] ## 類 Kotlin 中使用關鍵字`class`聲明類 ```kotlin class Invoice { /*……*/ } ``` 類聲明由**類名**、**類頭**(指定其類型參數、主構造函數等)以及由花括號包圍的**類體**構成。**類頭與類體都是可選**的; 如果一個類沒有類體,可以省略花括號。 ```kotlin class Empty ``` ### 構造函數 在 Kotlin 中的一個類可以有一個**主構造函數**以及一個或多個**次構造函數**。 * 主構造函數是類頭的一部分:它跟在類名(與可選的類型參數)后。 ```kotlin class Person constructor(firstName: String) { /*……*/ } ``` 如果主構造函數沒有任何注解或者可見性修飾符(默認的可見性修飾符時`public`。可以省略不寫),可以省略這個 *constructor*關鍵字。 ```kotlin class Person(firstName: String) { /*……*/ } ``` * 主構造函數不能包含任何的代碼。初始化的代碼可以放到以 *init*關鍵字作為前綴的**初始化塊(initializer blocks**)中。 在實例初始化期間,初始化塊按照它們出現在類體中的順序執行,與屬性初始化器交織在一起: ```kotlin //sampleStart class InitOrderDemo(name: String) { val firstProperty = "First property: $name".also(::println) init { // 主構造函數參數可以在初始化塊中使用 println("First initializer block that prints ${name}") } val secondProperty = "Second property: ${name.length}".also(::println) init { println("Second initializer block that prints ${name.length}") } } //sampleEnd fun main() { InitOrderDemo("hello") } ``` >[info]**注意**,主構造的參數可以在初始化塊中使用。它們也可以在類體內聲明的屬性初始化器中使用: ```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) { /*……*/ } ``` 更多詳情,參見[可見性修飾符](http://www.kotlincn.net/docs/reference/visibility-modifiers.html#%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0) #### 次構造函數 類也可以聲明前綴有 *constructor*的**次構造函數**: ```kotlin class Person { var children: MutableList<Person> = mutableListOf<Person>(); constructor(parent: Person) { parent.children.add(this) } } ``` * **同時存在主構造函數和二級構造函數時的情況** 如果類有一個主構造函數,每個次構造函數**需要委托給主構造函數**,可以直接委托或者通過別的次構造函數間接委托。委托到同一個類的另一個構造函數用 *this*關鍵字即可: ```kotlin class Person(val name: String) { var children: MutableList<Person> = mutableListOf<Person>(); constructor(name: String, parent: Person) : this(name) { parent.children.add(this) } } ``` 請注意,初始化塊中的代碼實際上會成為主構造函數的一部分。委托給主構造函數會作為次構造函數的第一條語句,因此**所有初始化塊中的代碼都會在次構造函數體之前執行。即使該類沒有主構造函數,這種委托仍會隱式發生,并且仍會執行初始化塊**: ```kotlin //sampleStart class Constructors { init { println("Init block") } constructor(i: Int) { println("Constructor") } } //sampleEnd fun main() { Constructors(1) } ``` 運行結果 ``` Init block Constructor ``` * **當類的主構造函數都存在默認值時的情況** 如果一個非抽象類沒有聲明任何(主或次)構造函數,它會生成一個不帶參數的主構造函數,構造函數的可見性是 public。如果你不希望你的類有一個公有構造函數,你需要聲明一個帶有非默認可見性(比如private)的空的主構造函數: ```kotlin class DontCreateMe private constructor () { /*……*/ } ``` > **注意**:在 JVM 上,如果主構造函數的所有的參數都有默認值,編譯器會生成一個額外的無參構造函數,它將使用默認值。這使得Kotlin 更易于使用像 Jackson 或者 JPA 這樣的通過無參構造函數創建類的實例的庫。 ``` class Customer(val customerName: String = "") ``` 由此可見:當類存在主構造函數并且有默認值時,次構造函數也適用。 * **示例** ~~~ fun main(args: Array<String>) { println("--------構造函數使用默認值--------") var test = Test() println("--------構造函數使用柱構造函數--------") var test1 = Test(1, 2) println("--------構造函數使用次構造函數--------") var test2 = Test(4, 5, 6) } class Test constructor(num1: Int = 10, num2: Int = 20) { init { println("num1 = $num1\t num2 = $num2") } constructor(num1: Int = 1, num2: Int = 2, num3: Int = 3) : this(num1, num2) { println("num1 = $num1\t num2 = $num2 \t num3 = $num3") } } ~~~ 運行結果 ``` --------構造函數使用默認值-------- num1 = 10 num2 = 20 --------構造函數使用柱構造函數-------- num1 = 1 num2 = 2 --------構造函數使用次構造函數-------- num1 = 4 num2 = 5 num1 = 4 num2 = 5 num3 = 6 ``` ### 創建類的實例 要創建一個類的實例,我們就像調用普通函數一樣調用構造函數: ```kotlin val invoice = Invoice() val customer = Customer("Joe Smith") ``` >[warning]注意: Kotlin 并沒有 *new*關鍵字。 創建嵌套類、內部類與匿名內部類的類實例在[嵌套類](http://www.kotlincn.net/docs/reference/nested-classes.html)中有述。 ### 類成員 類可以包含: * [構造函數與初始化塊](http://www.kotlincn.net/docs/reference/classes.html#%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0) * [函數](http://www.kotlincn.net/docs/reference/functions.html) * [屬性](http://www.kotlincn.net/docs/reference/properties.html) * [嵌套類與內部類](http://www.kotlincn.net/docs/reference/nested-classes.html) * [對象聲明](http://www.kotlincn.net/docs/reference/object-declarations.html) ## 繼承 在 Kotlin 中所有類都有一個共同的超類 `Any`,這對于沒有超類型聲明的類而言是默認超類,可參考[根類型“Any”](http://www.hmoore.net/alex_wsc/android_kotlin/1046286): ```kotlin class Example // 從 Any 隱式繼承 ``` `Any` 有三個方法:`equals()`、 `hashCode()` 與 `toString()`。因此,所有 Kotlin 類都定義了這些方法。 如需聲明一個顯式的超類型(open表示允許其它類繼承,和Java中final相反,**默認情況下,Kotlin所有類都是final**),請在類頭中把超類型放到冒號之后: ```kotlin open class Base(p: Int) class Derived(p: Int) : Base(p) ``` ### 繼承類的構造函數 如果派生類有一個主構造函數,其基類型可以(并且必須)用基類的主構造函數參數就地初始化。 如果派生類沒有主構造函數,那么每個次構造函數必須使用 *super* 關鍵字初始化其基類型,或委托給另一個構造函數做到這一點。 >[info]注意,在這種情況下,不同的次構造函數可以調用基類型的不同的構造函數: ```kotlin class MyView : View { constructor(ctx: Context) : super(ctx) constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs) } ``` #### 無主構造函數 如果派生類沒有主構造函數,那么每個次構造函數必須使用 *super* 關鍵字初始化其基類型,或委托給另一個構造函數做到這一點。 例:這里舉例在`Android`中常見的自定義View實現,我們熟知,當我們指定一個組件是,一般實現繼承類(基類型)的三個構造函數。 ``` class MyView : View(){ constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) } ``` 可以看出,當實現類無主構造函數時,分別使用了`super()`去實現了基類的三個構造函數。 #### 存在主構造函數 如果派生類有一個主構造函數,其基類型可以(并且必須)用基類的主構造函數參數就地初始化。 當存在主構造函數時,主構造函數一般實現基類型中參數最多的構造函數,參數少的構造函數則用`this`關鍵字引用即可了 ``` class MyView(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : View(context, attrs, defStyleAttr) { constructor(context: Context?) : this(context,null,0) constructor(context: Context?,attrs: AttributeSet?) : this(context,attrs,0) } ``` ### 覆蓋方法 我們之前提到過,Kotlin 力求清晰顯式。因此,Kotlin 對于可覆蓋的成員(我們稱之為*open*)以及覆蓋后的成員需要**顯式修飾符(open)**: ```kotlin open class Shape { open fun draw() { /*……*/ } fun fill() { /*……*/ } } class Circle() : Shape() { override fun draw() { /*……*/ } } ``` `Circle.draw()` 函數上必須加上 *override* 修飾符。如果沒寫,編譯器將會報錯。如果函數沒有標注 *open*如`Shape.fill()`,那么子類中不允許定義相同簽名的函數,不論加不加 **override**。將 *open*修飾符添加到 final 類(即沒有 *open*的類)的成員上不起作用。 標記為 *override*的成員本身是開放的,也就是說,它可以在子類中覆蓋。如果你想禁止再次覆蓋,使用 *final* 關鍵字: ```kotlin open class Rectangle() : Shape() { final override fun draw() { /*……*/ } } ``` 1. 當基類中的函數,沒有用`open`修飾符修飾的時候,實現類中出現的函數的函數名不能與基類中沒有用`open`修飾符修飾的函數的函數名相同,不管實現類中的該函數有無`override`修飾符修飾。 ``` open class Demo{ fun test(){} // 注意,這個函數沒有用open修飾符修飾 } class DemoTest : Demo(){ // 這里聲明一個和基類型無open修飾符修飾的函數,且函數名一致的函數 // fun test(){} 編輯器直接報紅,根本無法運行程序 // override fun test(){} 同樣報紅 } ``` 當一個類不是用`open`修飾符修飾時,這個類默認是`final`的,`class A{}`等價于`final class A{}` 注意,則的`final`修飾符在編輯器中是灰色的,因為Kotlin中默認的類默認是final的 2. 標記為 *override*的成員本身是開放的,也就是說,它可以在子類中覆蓋。如果你想禁止再次被覆蓋,使用 *final* 關鍵字。 ### 覆蓋屬性 屬性覆蓋與方法覆蓋類似;在超類中聲明然后在派生類中重新聲明的屬性必須以 *override*開頭,并且父類和子類它們必須具有兼容的數據類型。每個聲明的屬性可以由具有初始化器的屬性或者具有 `get` 方法的屬性覆蓋。 ```kotlin open class Shape { open val vertexCount: Int = 0 } class Rectangle : Shape() { override val vertexCount = 4//覆蓋了vertexCount,賦予它新的數值4 } ``` * 你也可以用一個 `var` 屬性覆蓋一個 `val` 屬性,但反之則不行。這是允許的,因為一個 `val` 屬性本質上聲明了一個 `get` 方法,而將其覆蓋為 `var` 只是在子類中額外聲明一個 `set` 方法。 * 請注意,你可以在主構造函數中使用 *override*關鍵字作為屬性聲明的一部分。 ```kotlin interface Shape { val vertexCount: Int } class Rectangle(override val vertexCount: Int = 4) : Shape // 總是有 4 個頂點 class Polygon : Shape { override var vertexCount: Int = 0 // 以后可以設置為任何數 } ``` * **Getter()函數慎用super關鍵字** ~~~ open class Demo { open val valStr = "我是用val修飾的屬性" } class DemoTest : Demo() { /* * 這里介紹重寫屬性是,getter()函數中使用`super`關鍵字的情況 */ override var valStr: String = "abc" get() = super.valStr set(value) { field = value } } fun main(arge: Array<String>) { println(DemoTest().valStr) val demo = DemoTest() demo.valStr = "1212121212" println(demo.valStr) } ~~~ 運行結果 ``` 我是用val修飾的屬性 我是用val修飾的屬性 ``` 從上面的執行結果,得出:在實際的項目中在重寫屬性的時候不用`get() = super.xxx`,因為這樣的話,不管你是否重新為該屬性賦了新值,它還是支持`setter()`,在使用的時候都調用的是基類中的屬性值。 * **在主構造函數中重寫** ``` open class Demo { open val num = 0 open val valStr = "我是用val修飾的屬性" } class DemoTest2(override var num: Int, override val valStr: String) : Demo() fun main(args: Array<String>) { val demo2 = DemoTest2(1, "構造函數中重寫") println("num = ${demo2.num} \t valStr = ${demo2.valStr}") } ``` 運行結果 ``` num = 1 valStr = 構造函數中重寫 ``` ### 派生類(子類)初始化順序 在構造派生類的新實例的過程中,第一步完成其基類的初始化(在此之前只有對基類構造函數參數的檢測),因此發生在派生類的初始化邏輯運行之前。 ~~~ //sampleStart open class Base(val name: String) { init { println("Initializing Base")//二 } open val size: Int = name.length.also { println("Initializing size in Base: $it") }//三 } class Derived( name: String, val lastName: String ) : Base(name.capitalize().also { println("Argument for Base: $it") }) {//一 init { println("Initializing Derived")//四 } override val size: Int = (super.size + lastName.length).also { println("Initializing size in Derived: $it") }//五 } //sampleEnd fun main() { println("Constructing Derived(\"hello\", \"world\")") val d = Derived("hello", "world") } ~~~ 執行結果,如上面代碼標注的順序一樣 ``` Constructing Derived("hello", "world") Argument for Base: Hello Initializing Base Initializing size in Base: 5 Initializing Derived Initializing size in Derived: 10 ``` 這意味著,基類(父類)構造函數執行時,派生類(子類)中聲明或覆蓋的屬性都還沒有初始化。如果在基類初始化邏輯中(直接或通過另一個覆蓋的 *open*成員的實現間接)使用了任何一個這種屬性,那么都可能導致不正確的行為或運行時故障。設計一個基類時,應該避免在構造函數、屬性初始化器以及 *init*塊中使用 *open*成員。 ![](https://img.kancloud.cn/13/a8/13a84bd8c99178e4edd685a9ed483911_1257x707.png) ### 調用超類(父類)實現 派生類(子類)中的代碼**可以使用 *super*關鍵字調用其超類的函數與屬性訪問器的實現**: ```kotlin open class Rectangle { open fun draw() { println("Drawing a rectangle") } val borderColor: String get() = "black" } class FilledRectangle : Rectangle() { override fun draw() { super.draw() println("Filling the rectangle") } val fillColor: String get() = super.borderColor } ``` 在一個內部類中訪問外部類的超類,可以通過由外部類名限定的 *super*關鍵字來實現:`super@Outer`: ```kotlin class FilledRectangle: Rectangle() { fun draw() { /* …… */ } val borderColor: String get() = "black" inner class Filler { fun fill() { /* …… */ } fun drawAndFill() { super@FilledRectangle.draw() // 調用 Rectangle 的 draw() 實現 fill() println("Drawn a filled rectangle with color ${super@FilledRectangle.borderColor}") // 使用 Rectangle 所實現的 borderColor 的 get() } } } ``` ### 覆蓋規則 在 Kotlin 中,實現繼承由下述規則規定:如果一個類從它的直接超類(父類)繼承相同成員的多個實現,它必須覆蓋這個成員并提供其自己的實現(也許用繼承來的其中之一)。 為了表示采用從哪個超類型繼承的實現,我們使用由尖括號中超類型名限定的 *super*,如 `super<Base>`: ```kotlin open class Rectangle { open fun draw() { /* …… */ } } interface Polygon { fun draw() { /* …… */ } // 接口成員默認就是“open”的 } class Square() : Rectangle(), Polygon { // 編譯器要求覆蓋 draw(): override fun draw() { super<Rectangle>.draw() // 調用 Rectangle.draw() super<Polygon>.draw() // 調用 Polygon.draw() } } ``` 可以同時繼承 `Rectangle` 與實現接口 `Polygon`,但是二者都有各自的 `draw()` 實現,所以我們必須在 `Square` 中覆蓋 `draw()`,并提供其自身的實現以消除歧義。 示例 ~~~ open class A { open fun test1() { println("基類A中的函數test1()") } open fun test2() { println("基類A中的函數test2()") } } interface B { fun test1() { println("接口類B中的函數test1()") } fun test2() { println("接口類B中的函數test2()") } } class C : A(), B { override fun test1() { super<A>.test1() super<B>.test1() } override fun test2() { super<A>.test2() super<B>.test2() } } fun main(args: Array<String>) { val C = C() C.test1() C.test2() } ~~~ 運行結果 ``` 基類A中的函數test1() 接口類B中的函數test1() 基類A中的函數test2() 接口類B中的函數test2() ``` ## 抽象類 類以及其中的某些成員可以聲明為 *abstract*。抽象成員在本類中可以不用實現。 需要注意的是,我們并不需要用 `open` 標注一個抽象類或者函數——因為這不言而喻。 我們可以用一個抽象成員覆蓋一個非抽象的開放成員 ```kotlin open class Polygon { open fun draw() {} } abstract class Rectangle : Polygon() { override abstract fun draw() } ``` ## 伴生對象 如果你需要寫一個可以無需用一個類的實例來調用、但需要訪問類內部的函數(例如,工廠方法),你可以把它寫成該類內[對象聲明](http://www.kotlincn.net/docs/reference/object-declarations.html)中的一員。 更具體地講,如果在你的類內聲明了一個[伴生對象](http://www.kotlincn.net/docs/reference/object-declarations.html#%E4%BC%B4%E7%94%9F%E5%AF%B9%E8%B1%A1),你就可以調用其成員,只是以類名作為限定符。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看