Kotlin彙總3-接口,可見作用域,擴展,數據類,密封類

1.接口

kotlin的接口比較像Java8,可以有實現的方法

interface MyInterface {
    fun bar()
    fun foo() {
      // optional body
    }
}

接口中的屬性可以是抽象的,也可以是提供實現的。

interface MyInterface {
    val prop: Int // abstract

    val propertyWithImplementation: String //實現的屬性
        get() = "foo"

    fun foo() {
        print(prop)
    }
}

另外需要注意如果一個類同時實現兩個接口,恰好這兩個接口都有同一個同樣的方法,那麼這個類,必須有自己的實現方式,這是Kotlin爲了避免衝突的解決方法。

interface A {
    fun foo() { print("A") }
    fun bar()
}

interface B {
    fun foo() { print("B") }
    fun bar() { print("bar") }
}

class C : A {
    override fun bar() { print("bar") }
}

class D : A, B {
    override fun foo() {
        super<A>.foo()
        super<B>.foo()
    }

    override fun bar() {
        super<B>.bar()
    }
}

2.可見作用域修飾符

kotlin有四種可見作用域修飾符:public,private,internal,protected,其中public是默認的,這和java的package可見作用域是默認的不一樣。

  • private 是 class類內可見
  • public 是任何地方可見
  • protected是private+子類可見
  • internal 是 module可以見,這個module包括:intellij module, maven/gradle project, 一個ant task的文件集

3. 擴展

擴展就像裝飾者模式,不需要繼承,給類增加新功能。

fun MutableList<Int>.swap(index1: Int, index2: Int) {
    val tmp = this[index1] // 'this' corresponds to the list
    this[index1] = this[index2]
    this[index2] = tmp
}

一般寫法是類名<泛型>.方法,所以你可以理解爲static調用的方式。

open class C

class D: C()

fun C.foo() = "c"

fun D.foo() = "d"

fun printFoo(c: C) {
    println(c.foo())
}

printFoo(D())

上面的結果是打印c,因爲擴展,它依賴於函數定義時傳遞的類型(如上是C),而不是運行時傳遞的類型(如上是D)

class C {
    fun foo() { println("member") }
}

fun C.foo() { println("extension") }

如果執行c.foo(),那麼結果會是member,就是說如果擴展了同樣的方法(包括返回類型,方法名,參數名),那麼實際上這個擴展是沒有意義的。

但是如果增加了一個覆載的方法就不一養

class C {
    fun foo() { println("member") }
}

fun C.foo(i: Int) { println("extension") }

如果執行c.foo(1), 結果會打印extension,因爲擴展了C類中沒有的方法,因而這個方法是有效地。

而且還有擴展屬性

val <T> List<T>.lastIndex: Int
    get() = size - 1

但是需要注意屬性擴展只能使用set/get,而不能直接初始化,比如下面代碼是錯誤的

val Foo.bar = 1 // error: initializers are not allowed for extension properties

一般情況下擴展會應用到top-level

package foo.bar

fun Baz.goo() { ... } 

也可以定義到類裏作爲成員函數

class D {
    fun bar() { ... }
}

class C {
    fun baz() { ... }

    fun D.foo() {
        bar()   // calls D.bar
        baz()   // calls C.baz
    }

    fun caller(d: D) {
        d.foo()   // call the extension function
    }
}

在擴展裏調用它所在類的方法需要如下(this@C)

class C {
    fun D.foo() {
        toString()         // calls D.toString()
        this@C.toString()  // calls C.toString()
    }

擴展的目的是爲了簡化代碼如下:

// Java
Collections.swap(list, Collections.binarySearch(list, Collections.max(otherList)), Collections.max(list))

//kotlin擴展簡化後,代碼可閱讀性也提高了
// Java
list.swap(list.binarySearch(otherList.max()), list.max())

3.數據類

有時候定義一個類僅僅是爲了使用它的數據,那麼可以定義數據類

data class User(val name: String, val age: Int) // 使用data關鍵字

然後編譯器會幫我們創建好equals/hashcode, toString(),copy方法

fun copy(name: String = this.name, age: Int = this.age) = User(name, age) 

val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)

4.密封類

密封類是爲了維護嚴格的類層次關係。使用sealed關鍵字修飾,密封類可以有子類,但是它和它的子類都必須在一個kotlin文件中聲明.

sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()

fun eval(expr: Expr): Double = when (expr) {
    is Const -> expr.number
    is Sum -> eval(expr.e1) + eval(expr.e2)
    NotANumber -> Double.NaN
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章