Kotlin 擴充函數 與 JS 的 prototype
Kotlin 擴充函數
Kotlin的擴充函數功能使得我們可以為現有的類添加新的函數,實作某一具體功能 。
擴充函數是靜态解析的,并未對原類添加函數或屬性,對類本身沒有任何影響。
擴充屬性允許定義在類或者kotlin檔案中,不允許定義在函數中。
lambda是要作為參數被傳入某方法或指派給某變量的匿名方法的簡化表現形式。
fun Activity.toast(message: CharSequence, duration: Int = Toast.LENGTH_SHORT){
Toast.makeText(this, message, duration).show()
}
this指接收者對象(receiver object)(也就是調用擴充函數時, 在"."号之前指定的對象執行個體).
fun Any?.toString():String{
if(this == null)
return "null"
else{
return toString()
}
}
1.擴充(extensions)
在不修改原類的情況下,
Kotlin能給一個類擴充新功能,無需繼承該類,也不用任何設計模式(如裝飾模式等),
Kotlin支援擴充函數和擴充屬性!
為什麼要使用擴充(動機):
在Java中,有很多工具類如java.util.Collections,使用很繁瑣:
// Java
Collections.swap(list, Collections.binarySearch(list, Collections.max(otherList)), Collections.max(list))
靜态導入Collections類,簡化寫法:
// Java
swap(list, binarySearch(list, max(otherList)), max(list))
靜态導入使用依然很麻煩,如果能給list類添加擴充函數就好了:
list.swap(list.binarySearch(otherList.max()), list.max())
2.類-擴充函數
1.定義
為MutableList類擴充一個swap函數:
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1] //this: 目前MutableList對象
this[index1] = this[index2]
this[index2] = tmp
}
對MutableList對象調用swap函數:
val list = mutableListOf(1, 2, 3)
list.swap(0, 2)
MutableList泛化類型:
//為在表達式中使用泛型,要在函數名前添加泛型參數!
fun <T> MutableList<T>.swap(index1: Int, index2: Int) {
val tmp = this[index1]
this[index1] = this[index2]
this[index2] = tmp
}
2.靜态解析(沒有多态)
擴充不能真正修改類,即沒有在一個類中插入新成員!
擴充函數是靜态解析分發的,不是虛函數(即沒有多态),調用隻取決于對象的聲明類型!
1.調用是由對象聲明類型決定,而不是由對象實際類型決定!
open class C
class D: C()
fun C.foo() = "c"
fun D.foo() = "d"
fun printFoo(c: C) {
println(c.foo()) //擴充函數是靜态解析的,不是虛函數(即沒有多态)
}
fun main(args: Array<String>) {
printFoo(D()) //輸出"c",擴充函數調用隻取決于參數c的聲明類型
}
2.類的成員函數和擴充函數-同名同參數:
class C {
fun foo() { println("member") }
}
fun C.foo() {
println("extension")
}
fun main(args: Array<String>) {
val c = C()
println(c.foo()) //輸出“member”
}
3.類的成員函數和擴充函數-同名不同參數:
class C {
fun foo() { println("member") }
}
fun C.foo(i: Int) {
println("extension")
}
fun main(args: Array<String>) {
val c = C()
println(c.foo(2)) //輸出"extension"
}
3.可空接收者
可null的類型定義擴充,即使對象為null,也可在對象上調用!
fun Any?.toString(): String {
if (this == null) return "null"
return toString()
}
2.類-擴充屬性
和擴充函數類似,Kotlin也支援擴充屬性:
val <T> List<T>.lastIndex: Int // 不能初始化
get() = size - 1 // 隻能由getters/setters顯式提供
val Foo.bar = 1 // 錯誤:擴充屬性不能有初始化器
get() = 1
由于擴充沒有在類中插入新成員,是以擴充屬性無法使用幕後字段,
這就是為什麼擴充屬性不能有初始化器,隻能由getters/setters顯式提供!
3.伴生對象-擴充函數和屬性
可為伴生對象定義擴充函數和屬性:
class MyClass {
companion object { } //伴生對象
}
fun MyClass.Companion.foo() {
// ……
}
MyClass.foo() //用類名調用
4.作用域
1.擴充直接在包中
在頂層定義擴充(即直接在包中):
package foo.bar
fun Baz.goo() {
...
}
在其它包調用:
package com.example.usage
import foo.bar.goo //導入所有名為“goo”的擴充
// 或者 import foo.bar.*
fun usage(baz: Baz) {
baz.goo()
}
2.擴充作為類成員
在一個類内部可為另一個類聲明擴充,
擴充聲明所在的類稱為分發接收者(dispatch receiver),
擴充函數調用所在類稱為擴充接收者(extension receiver)
class D { //擴充接收者(extension receiver)
fun f() { …… }
}
class C { //分發接收者(dispatch receiver)
fun f() { …… }
fun D.foo() {
[email protected]() //分發接收者 C.f()
f() //擴充接收者 D.f()
}
fun call(d: D) {
d.foo() //調用擴充函數
}
}
2.繼承-覆寫
成員擴充可聲明為open,并在子類中被覆寫,
對分發接收者是虛拟的(多态),但對擴充接收者是靜态的!
open class D {
}
class D1 : D() {
}
open class C {
open fun D.foo() {
println("D.foo in C")
}
open fun D1.foo() {
println("D1.foo in C")
}
fun call(d: D) {
d.foo() // 調用擴充函數
}
}
class C1 : C() {
override fun D.foo() {
println("D.foo in C1")
}
override fun D1.foo() {
println("D1.foo in C1")
}
}
C().call(D()) // 輸出 "D.foo in C"
C().call(D1()) // 輸出 "D.foo in C", 擴充接收者靜态解析(非多态)
C1().call(D()) // 輸出 "D.foo in C1",分發接收者虛拟解析(多态)
JS 的 prototype
JavaScript prototype 屬性
定義和用法
prototype 屬性使您有能力向對象添加屬性和方法。
prototype就是“一個給類的對象添加方法的方法”,使用prototype屬性,可以給類動态地添加方法
文法
object.prototype.name=value
執行個體
在本例中,我們将展示如何使用 prototype 屬性來向對象添加屬性:
function employee(name,job,born){
this.name=name;
this.job=job;
this.born=born;
}
var bill=new employee("Bill Gates","Engineer",1985);
employee.prototype.salary=null;
bill.salary=20000;
console.log(bill.salary);
輸出:
20000