1. 擴展函數(shù)
Kotlin的擴展函數(shù)可以讓你作為一個類成員進行調(diào)用的函數(shù)假哎,但是是定義在這個類的外部瞬捕。這樣可以很方便的擴展一個已經(jīng)存在的類,為它添加額外的方法舵抹。在Kotlin源碼中肪虎,有大量的擴展函數(shù)來擴展java,這樣使得Kotlin比java更方便使用惧蛹,效率更高扇救。通常在java中,我們是以各種XXXUtils的方式來對已經(jīng)存在的類進行功能的擴展香嗓。但是有了擴展函數(shù)迅腔,我們就能丟棄讓人討厭的XXXUtils方法工具類。下面舉個例子靠娱,假如我們需要為String類型添加一個返回這個字符串最后一個字符的方法:
package com.dengyin2000.kotlintest1
fun String.lastChar(): Char = this.get(this.length - 1)
fun main(args: Array<String>) {
println("Kotlin".lastChar())
}
你只需要在你添加的函數(shù)名字之前放置你想要擴展的類或者接口的類型钾挟。這個類名叫著接收器類型(receiver type),而你調(diào)用的擴展函數(shù)的值叫做接收器對象(receiver object)饱岸。如下圖:
接收器類型是擴展定義的類型,而接收器對象是這個類型的實例徽千。調(diào)用方式跟普通的函數(shù)調(diào)用方式一致:
println("Kotlin".lastChar())
在這個例子中苫费,String
是接收器類型,"Kotlin"
接收器對象双抽,在這個擴展函數(shù)中百框,你可以直接訪問你擴展的類型的函數(shù)和屬性,就像定義在這個類中的方法一樣牍汹,但是擴展函數(shù)并不允許你打破封裝铐维。跟定義在類中方法不同柬泽,它不能訪問那些私有的、受保護的方法和屬性嫁蛇。
1.1 擴展函數(shù)的導(dǎo)入
大多數(shù)情況下锨并,我們直接在包里定義擴展函數(shù)。這樣我們就可以在整個包里面使用這些擴展睬棚,如果我們要使用其他包的擴展第煮,我們就需要導(dǎo)入它。導(dǎo)入擴展函數(shù)跟導(dǎo)入類是一樣的方式抑党。
import com.dengyin2000.kotlintest1.lastChar
或者
import com.dengyin2000.kotlintest1.*
有時候包警,可能你引入的第三方包都對同一個類型進行了相同函數(shù)名擴展,為了解決沖突問題底靠,你可以使用下面的方式對擴展函數(shù)進行改名害晦。
package com.dengyin2000.kotlintest2
import com.dengyin2000.kotlintest1.lastChar as last
fun main(args: Array<String>) {
println("Kotlin".last())
}
1.2 范型化的擴展函數(shù)
我們也可以在對擴展函數(shù)進行范型化。
package com.dengyin2000.kotlintest1
fun String.lastChar(): Char = this.get(this.length - 1)
fun <T> Collection<T>.joinToString(
separator: String = ",",
prefix: String = "",
postfix: String = ""
): String{
val result = StringBuilder(prefix)
for ((index, value) in this.withIndex()) {
if (index > 0) {
result.append(separator)
}
result.append(value)
}
result.append(postfix)
return result.toString()
}
fun main(args: Array<String>) {
println(listOf("a", "b", "c").joinToString(prefix = "[", postfix = "]"))
}
輸出:
[a,b,c]
1.3 擴展函數(shù)不可覆蓋(overriding)
方法的覆蓋(overriding)對類中的成員函數(shù)是有效的暑中,但是擴展函數(shù)不能被覆蓋壹瘟,請看下面這個例子:
package com.dengyin2000.kotlintest1
open class View{
open fun click() {
println("view clicked")
}
}
open class Button: View() {
override fun click(){
println("button clicked")
}
}
fun View.longClick() = println("view longClicked")
fun Button.longClick() = println("button longClicked")
fun main(args: Array<String>) {
val button:View = Button()
button.click()
button.longClick()
}
輸出:
button clicked
view longClicked
可以看到擴展函數(shù)并不能被覆蓋,我們把變量定義成View痒芝,longClick()使用的是View.longClick()擴展函數(shù)俐筋。擴展函數(shù)并不是類的一部分,他們申明在類的外部严衬。盡管你可以為某個基類和它的之類用同樣的名字和參數(shù)來定義擴展函數(shù)澄者,被調(diào)用的函數(shù)依賴已被申明的靜態(tài)類型,而不是運行時的變量類型请琳。
1.4 Java調(diào)用擴展函數(shù)
調(diào)用一個擴展函數(shù)并沒有涉及對象的創(chuàng)建或者其他運行時開銷粱挡,在底層,一個擴展函數(shù)是一個接收器對象作為第一個參數(shù)的靜態(tài)方法俄精。這讓我們在java調(diào)用擴展函數(shù)就像調(diào)用靜態(tài)方法一樣询筏,假如我們的擴展函數(shù)定義在com.dengyin2000.kotlintest1.test.kt
文件中。那么會生成一個com.dengyin2000.kotlintest1.TestKt.class
java類文件竖慧,其中就包含了我們在Kotlin中定義的那兩個擴展函數(shù)嫌套。如圖:
下面就是在Java中調(diào)用Kotlin擴展函數(shù)的方法:
package com.dengyin2000.java;
import com.dengyin2000.kotlintest1.Button;
import com.dengyin2000.kotlintest1.TestKt;
import com.dengyin2000.kotlintest1.View;
public class Test {
public static void main(String[] args) {
View view = new Button();
TestKt.longClick(view);
}
}
看到這里你應(yīng)該也明白了,為什么擴展函數(shù)不能覆蓋(overriding)了圾旨。
2. 擴展屬性
擴展屬性提供了一種方法用能通過屬性語法進行訪問的API來擴展踱讨。盡管它們被叫做屬性,但是它們不能擁有任何狀態(tài)砍的,它不能添加額外的字段到現(xiàn)有的java對象實例痹筛。不過可以有更簡短的語法在某些時候還是更方便的。
package com.dengyin2000.kotlintest1
val String.lastChar: Char
get() = get(length - 1)
var StringBuilder.lastChar: Char
get() = get(length - 1)
set(value) {
this.setCharAt(length -1, value)
}
fun main(args: Array<String>) {
println("Kotlin".lastChar)
val sb = StringBuilder("Kotlin")
sb.lastChar = 'g'
println(sb)
}
可以看到擴展屬性也可以通過val或者var定義,然后也是接你需要擴展的類型帚稠,然后屬性名稱谣旁,最后是屬性的類型。var的話可以有set方法定義滋早。你訪問擴展屬性和訪問成員屬性完全一樣榄审。