Extension 既擴展方法,若寫過動態(tài)語言就一定知道
class Foo{}
foo = new Foo()
foo.func = funtion (args){
//do something
}
之后就可以調(diào)用foo.func()函數(shù)這就是動態(tài)擴展
上面的例子是給實例擴展當然動態(tài)語言給類擴展也是沒有問題的,而kotlin中可以對類進行擴展
以類成員函數(shù)的方式聲明
package Bar.Foo
class Father(){
fun takeMoney(kiss:Kiss):Money{
return SelfMoney.half
}
fun sleep(){}
}
class Mather(){
//Father's extension
fun Father().buyGucci():Gucci{
val kiss = giveKiss()
val money = takeMoney(kiss)
//pay money
sleep()//father.sleep()
this@Mather.sleep()//mather.sleep() and this@Mather == Mather.this in java
return gucci
}
fun wantHappy(){
val husband = Father()
husband.buyGucci()
}
fun giveKiss():Kiss{
return kiss
}
fun sleep(){}
}
首先說明,kotlin中也有包的概念但是包下頂級元素不需要是類,kotlin中的第一公民是函數(shù)。
從上面的代碼中我想說明以下幾點擴展函數(shù)的特性
擴展函數(shù)是對類的擴展,對于Father來說相當于在自己里面定義了buyGucci函數(shù)
-
同時擴展函數(shù)中隱含著兩個引用,既有Mather的實例的引用(dispatch receiver
)又有father實例的引用(extension receiver)所以可以直接調(diào)用兩個類的函數(shù)以及屬性
在其他任意地方調(diào)用類的擴展都需要得到類的實例
當dispatch receiver和extension receiver中有相同的函數(shù)簽名,擴展方法優(yōu)先調(diào)用extension receiver中的方法,除非指定dispatch receiver
作用域問題
擴展方法可以直接獨立在頂級包中定義
package foo.bar
fun Baz.goo() { ... }
要在包外調(diào)用擴展方法需要import這個方法或者包
package com.example.usage
import foo.bar.goo // importing all extensions by name "goo"
// or
import foo.bar.* // importing everything from "foo.bar"
fun usage(baz: Baz) {
baz.goo()
)
當你在兩個頂級包內(nèi)定義了函數(shù)簽名相同的函數(shù),比如
package A
fun String.ok():String {
return "ok"
}
package B
fun String.ok():String{
return "OK"
}
這種情況會直接報錯,如果在頂級包名和一個類中定義了相同的擴展函數(shù)
package A
fun String.ok():String{
return "ok"
}
package B
class Foo(){
fun String.ok():String{
return "OK"
}
}
package C
fun main(s:Array<String>){
println("Ara you ok?".ok())
}
result:
ok // by Top level package
在類中以成員方式定義的擴展方法只能在類的作用域內(nèi)被調(diào)用
比如類Foo中,以及Foo的擴展函數(shù)中fun Foo.xxx(){"ok?".ok()})
如果是在Foo中調(diào)用ok,成員擴展優(yōu)先于頂級擴展,成員函數(shù)也優(yōu)先于擴展函數(shù)
class C {
fun foo() { println("member") }
}
fun C.foo() { println("extension") }
上面這個例子不論在任何使用調(diào)用foo函數(shù)結(jié)果都是member
擴展函數(shù)的靜態(tài)性
open class C //kotlin 默認不能繼承要加open修飾符
class D: C()
fun C.foo() = "c" //函數(shù)為第一公民當然可以對函數(shù)賦值,既返回c
fun D.foo() = "d"
fun printFoo(c: C) {
println(c.foo())
}
printFoo(D())
result:
c
上面這個例子說明了成員函數(shù)是靜態(tài)的在你聲明參數(shù)類型的時候就決定了使用哪個成員函數(shù)而不是取決于你傳入的實際值
對友元擴展
kotlin中沒有static關(guān)鍵字也沒有實際上的靜態(tài)概念,如果想像java那樣調(diào)用靜態(tài)方法和靜態(tài)成員可以用友元
class My{
companion object(){
val I = 1
fun getNum():Int{
return I
}
}
}
fun main(args:Array<String>){
val num = My.getNum()
val i = My.I
println(num == i)
}
result:
true
友元也可以進行擴展只要像下面那樣寫
class MyClass {
companion object { } // will be called "Companion"
}
fun MyClass.Companion.foo() {
// ...
}
擴展屬性
可以給任意類擴展一個屬性
val <T> List<T>.lastIndex: Int
get() = size - 1
但是擴展的屬性并不是實際上在類里插入變量,所以沒有好的方法給它分配實際的存儲空間,所以擴展成員不能被賦值只能重寫他的get方法,其實這就和一個函數(shù)一樣了
空類型接收器
kotlin 中默認的變量是 not null 的如果這個函數(shù)可能為null就要在類型后面加?比如String?就代表這個字符串有可能是null,而我們也可以給這種類型加擴展,那么我們處理null判斷的方法就可以寫成這樣的擴展形式,這樣你就可以放心的調(diào)用toString()而不用判斷是否為空
fun Any?.toString(): String {
if (this == null) return "null"
// after the null check, 'this' is autocast to a non-null type, so the toString() below
// resolves to the member function of the Any class
return toString()
}
Extension的應(yīng)用場景
-
可以改造一些原生的class 而不用寫一個Utils類,比如給String加一個格式化日期擴展
fun String().toDate(format:String = "yyyy-MM-dd HH:mm:ss") :String{ val sdf = SimpleDateFormat(format) val date = sdf.parse(value) return date.toString() } fun main(args:Array<String>){ println("UTC format Date".toDate())//2017-11-11 11:11:11 }?
-
可以寫一些大部分類都通用的routine
//例如: fun doSomething(){ try{ //doing }catch(e:Exception){ //error } } //這樣的代碼在java中有大把而且很多人其實catch了之后也不會做什么特殊的處理當代碼中到處充斥這種結(jié)構(gòu)甚至嵌套好幾層其實無形增加的閱讀的難度和維護的難度 //extension way fun <T> T.dowithTry(work:(T)->Unit){ try{ work(T) }catch(e:Exception){ //print error etc. } } fun <T:Closeable> T.dowithTry(work:(T)->Unit){ try{ work(T) }catch(e:Exception){ //print error etc. }finally{ this.close() } } //那么你只要這么調(diào)用,甚至臉close都幫你做了 fun main(arg:Array<String>){ val output = File().outputStream() output.dowithTry{ it -> it.read() .... } } 實現(xiàn)一些函數(shù)式編程的魔法
結(jié)語
擴展是個很有意思的東西發(fā)揮想想力能寫出很多提升效率和閱讀性擴展函數(shù)