頻道欄目
首頁 > 資訊 > 其他 > 正文

Kotlin:類與集成

16-10-20        來源:[db:作者]  
收藏   我要投稿

使用class 關鍵字定義:

class Invoice{
}
類的定義由以下幾部分組成:類名, 類頭部(指定類的類型參數, 主構造器, 等等.), 以及由大括號括起的類主體部分. 類的頭部和主體部分都是可選的; 如果類沒有主體部分, 那么大括號也可以省略。
class Invoive
構造器
Kotlin 中的類可以有一個主構造器 (primary constructor), 以及一個或多個次構造器 (secondary constructor). 主構造器是類頭部的一部分, 位于類名稱(以及可選的類型參數)之后.
class Person constructor(firstName: String) {
}
如果主構造器沒有任何注解(annotation), 也沒有任何可見度修飾符, 那么 constructor 關鍵字可以省略:
class Person(firstName: String) {
}
主構造器中不能包含任何代碼. 初始化代碼可以放在 初始化代碼段 (initializer block) 中, 初始化代碼段使用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) { ... }
次級構造器(secondary constructor)
類還可以聲明 次級構造器 (secondary constructor), 使用 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. 如果不希望你的類帶有 public 的構造器, 你需要聲明一個空的構造器, 并明確設置其可見度:
class DontCreateMe private constructor () {
}
注意: 在 JVM 中, 如果主構造器的所有參數都指定了默認值, 編譯器將會產生一個額外的無參數構造器, 這個無參數構造器會使用默認參數值來調用既有的構造器. 有些庫(比如 Jackson 或 JPA) 會使用無參數構造器來創建對象實例, 這個特性將使得 Kotlin 比較容易與這種庫協同工作.
class Customer(val customerName: String = "")
創建類的實例
要創建一個類的實例, 我們需要調用類的構造器, 調用方式與使用通常的函數一樣:
val invoice = Invoice()
val customer = Customer("Joe Smith")
注意, Kotlin 沒有 new 關鍵字.
類成員
類中可以包含以下內容:
構造器和初始化代碼塊函數屬性嵌套類和內部類對象聲明
繼承

Kotlin 中所有的類都有一個共同的超類 Any, 如果類聲明時沒有指定超類, 則默認為 Any:

class Example // 隱含地繼承自 Any

Any 不是 java.lang.Object; 尤其要注意, 除 equals(), hashCode() 和 toString() 之外, 它沒有任何成員. 詳情請參見 與 Java 的互操作性.

要明確聲明類的超類, 我們在類的頭部添加一個冒號, 冒號之后指定超類:
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 注解(annotation) 與 Java 的 final 正好相反: 這個注解表示允許從這個類繼承出其他子類. 默認情況下, Kotlin 中所有的類都是 final 的, 這種設計符合 Effective Java, 一書中的第 17 條原則: 允許繼承的地方, 應該明確設計, 并通過文檔注明, 否則應該禁止繼承.

成員的覆蓋

我們在前面提到過, 我們很注意讓 Kotlin 中的一切都明白無誤. 而且與 Java 不同, Kotlin 要求明確地注解來標識允許被子類覆蓋的成員(我們稱之為 open), 而且也要求明確地注解來標識對超類成員的覆蓋:
open class Base {
  open fun v() {}
  fun nv() {}
}
class Derived() : Base() {
  override fun v() {}
}

對于 Derived.v() 必須添加 override 注解. 如果遺漏了這個注解, 編譯器將會報告錯誤. 如果一個函數沒有標注 open 注解, 比如上例中的 Base.nv(), 那么在子類中聲明一個同名同參的方法將是非法的, 無論是否添加 override 注解, 都不可以. 在一個 final 類(比如, 一個沒有添加 open 注解的類)中, 聲明 open 成員是禁止的.

當一個子類成員標記了 override 注解來覆蓋父類成員時, 覆蓋后的子類成員本身也將是 open 的, 也就是說, 子類成員可以被自己的子類再次覆蓋. 如果你希望禁止這種再次覆蓋, 可以使用 final 關鍵字:
open class AnotherDerived() : Base() {
  final override fun v() {}
}
屬性的覆蓋方式與方法覆蓋類似. 注意, 你可以在主構造器的屬性聲明中使用 override 關鍵字:
open class Foo {
    open val x: Int get { ... }
}


class Bar1(override val x: Int) : Foo() {

}

你也可以使用一個 var 屬性覆蓋一個 val 屬性, 但不可以反過來使用一個 val 屬性覆蓋一個 var 屬性.允許這種覆蓋的原因是, val 屬性本質上只是定義了一個 get 方法, 使用 var 屬性來覆蓋它, 只是向后代類中添加了一個 set 方法.

如何才能 hack 我用到的那些類庫呢?!

你可能曾經習慣于從類庫中的某個類繼承一個子類, 然后覆蓋掉類庫設計者并不期望你覆蓋的某些方法, 以這種比較骯臟的手段來 hack 類庫. 但在 Kotlin 中, 類與成員默認都是 final 的, 我們針對繼承和覆蓋問題所選擇的這種設計原則, 將會帶來一個問題, 就是前面所說的那種類庫 hack 方式將會變得比較困難.

但我們認為這并不是一個缺點, 理由如下: 程序設計的最佳實踐原則認為, 你本來就不應該使用這種 hack 手段其他語言(比如 C++, C#)也使用了類似的原則, 大家使用起來并未遇到問題如果有人確實希望 hack, 仍然存在其他方法: 你可以用 Java 來編寫你的 hack 代碼, 然后通過 Kotlin 來調用(參見 與 Java 的互操作性). 另外 Aspect 框架就是為這類目的設計的, 你可以使用 Aspect 框架來解決這類問題.

覆蓋的規則

在 Kotlin 中, 類繼承中的方法實現問題, 遵守以下規則: 如果一個類從它的直接超類中繼承了同一個成員的多個實現, 那么這個子類必須覆蓋這個成員, 并提供一個自己的實現(可以使用繼承得到的多個實現中的某一個). 為了表示使用的方法是從哪個超類繼承得到的, 我們使用 super 關鍵字, 將超類名稱放在尖括號類, 比如, super :
open class A {
  open fun f() { print("A") }
  fun a() { print("a") }
}

interface B {
  fun f() { print("B") } // 接口的成員默認是 'open' 的
  fun b() { print("b") }
}

class C() : A(), B {
  // 編譯器要求 f() 方法必須覆蓋:
  override fun f() {
    super.f() // 調用 A.f()
    super.f() // 調用 B.f()
  }
}
同時繼承 A 和 B 是合法的, 而且函數 a() 和 b() 的繼承也不存在問題, 因為對于這兩個函數, C 類都只繼承得到了唯一的一個實現. 但對函數 f() 的繼承就發生了問題, 因為 C 類從超類中繼承得到了兩個實現, 因此在 C 類中我們必須覆蓋函數 f(), 并提供我們自己的實現, 這樣才能消除歧義.

抽象類

類本身, 或類中的部分成員, 都可以聲明為 abstract 的. 抽象成員在類中不存在具體的實現. 注意, 我們不必對抽象類或抽象成員標注 open 注解 – 因為它顯然必須是 open 的.

我們可以使用抽象成員來覆蓋一個非抽象的 open 成員:

open class Base {

open fun f() {}

}

abstract class Derived : Base() {

override abstract fun f()

}

同伴對象(Companion Object)

與 Java 或 C# 不同, Kotlin 的類沒有靜態方法(static method). 大多數情況下, 建議使用包級函數(package-level function)替代靜態方法.

如果你需要寫一個函數, 希望使用者不必通過類的實例來調用它, 但又需要訪問類的內部信息(比如, 一個工廠方法), 你可以將這個函數寫為這個類之內的一個對象聲明 的成員, 而不是類本身的成員.

具體來說, 如果你在類中聲明一個 同伴對象, 那么只需要使用類名作為限定符就可以調用同伴對象的成員了, 語法與 Java/C# 中調用類的靜態方法一樣.

封閉類(Sealed Class)

封閉類(Sealed class)用來表示對類階層的限制, 可以限定一個值只允許是某些指定的類型之一, 而不允許是其他類型. 感覺上, 封閉類是枚舉類(enum class)的一種擴展: 枚舉類的值也是有限的, 但每一個枚舉值常數都只存在唯一的一個實例, 封閉類則不同, 它允許的子類類型是有限的, 但子類可以有多個實例, 每個實例都可以包含它自己的狀態數據.

要聲明一個封閉類, 需要將 sealed 修飾符放在類名之前. 封閉類可以有子類, 但所有的子類聲明都必須嵌套在封閉類的聲明部分之內.

sealed class Expr {
    class Const(val number: Double) : Expr()
    class Sum(val e1: Expr, val e2: Expr) : Expr()
    object NotANumber : Expr()
}
注意, 從封閉類的子類再繼承的子類(間接繼承者)可以放在任何地方, 不必在封閉類的聲明部分之內.

使用封閉類的主要好處在于, 當使用 when expression 時, 可以驗證分支語句覆蓋了所有的可能情況, 因此就不必通過 else 分支來處理例外情況.

fun eval(expr: Expr): Double = when(expr) {
    is Expr.Const -> expr.number
    is Expr.Sum -> eval(expr.e1) + eval(expr.e2)
    Expr.NotANumber -> Double.NaN
    // 不需要 `else` 分支, 因為我們已經覆蓋了所有的可能情況
}
相關TAG標簽
上一篇:java并發包詳解(jdk7)
下一篇:Java Web學習總結(28)——Web項目MVC開源框架SSH和SSM比較
相關文章
圖文推薦

關于我們 | 聯系我們 | 廣告服務 | 投資合作 | 版權申明 | 在線幫助 | 網站地圖 | 作品發布 | Vip技術培訓 | 舉報中心

版權所有: 紅黑聯盟--致力于做實用的IT技術學習網站

美女MM131爽爽爽毛片