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
}