kotlin 擴展(擴展函數和擴展屬性)

前言

在java中咱們須要擴展一個類的新功能時,通常是繼承該類或者使用像裝飾者這樣的設計模式來實現的。
以下:java

public class Animal {
    protected String name;
    Animal(String name){
        this.name = name;
    }

    public void showName(){
        System.out.print(name);
    }
}
複製代碼

咱們想要給這個 類加一個吃東西的功能,這時使用Java繼承來實現
實現代碼以下:git

public class Cat extends Animal {
    Cat(String name) {
        super(name);
    }
    //新增長吃東西的功能
    public void eat(String food){
        System.out.print(name+":吃了 "+food);
    }

}
複製代碼

這樣咱們就實現了 吃東西的功能。
而在kotlin咱們不用這樣實現了,能夠經過叫作 ++擴展++ 的特殊聲明來完成。Kotlin 支持 ++擴展函數++ 和 ++擴展屬性++。github

1.擴展函數

聲明一個擴展函數咱們須要用一個 被擴展的類來做爲它的前綴。
公式以下:設計模式

fun 被擴展類名.擴展函數名( 參數 ){app

//實現代碼函數

}ui

咱們來實現上面java實現的功能,代碼以下:this

fun Animal.eat(food:String){
    print("$name 吃了 $food")
}

複製代碼

上面kotlin代碼就實現了咱們用java繼承實現的新功能,那要怎麼調用呢 咱們能夠在kotlin中像調用普通的函數來調用這個擴展函數,代碼以下:spa

val animal = Animal("cat")
 animal.eat("apple")
複製代碼

上面介紹了kotlin中怎麼調用擴展函數,那在Java中怎麼調用了,代碼以下:設計

Animal animal = new Animal("cat");
        //java 中調用kotlin 擴展函數 Aaa 爲擴展函數文件名
        AaaKt.eat(animal,"apple");
複製代碼

這樣咱們就在不用繼承該類的狀況下增長了 吃東西的新功能。

注意: 咱們能夠從上面的java調用中能夠看出,擴展並非真正的修改了被擴展類。而只是在kotlin中的調用像是修改了被擴展類。

1.2.擴展是靜態解析的

在kotlin中擴展是靜態分發的,不是根據接收者類型的虛方法。也就是說調用擴展函數是由調用所在的表達式類型來決定的,而不是由表達式運行時求值結果決定的。例如:

//擴展了 Animal 和Cat Cat 繼承 Animal 
    fun Animal.getFood() = "蘋果"
    fun Cat.getFood() = "貓糧"
    //在這個方法中 傳入類型是 Animal 因此獲取到的是"蘋果",無論是傳入的是Animal 仍是 Animal 的子類 如Cat
    fun printFood(food:Animal){
        print(food.getFood())
    }
    
    fun main(args: Array<String>) {
      val cat = Cat("cat")
      //如我在這傳入的是 Cat 可是 輸出出來的仍是 蘋果
        printFood(cat)
    }
    
複製代碼

在這個例子會輸出「蘋果」,由於咱們在調用擴展函數時只取決於參數 food 的聲明類型,該類型是 Animal 類。

若是擴展函數和被擴展類中的成員函數有相同的接收類型、名字和參數,那麼這種狀況下 ** 老是取成員**。例如:

class Dog {

    fun showName(){
        print("Dog")
    }
}

fun Dog.showName(){
    print("Cat")
}

fun main(args: Array<String>) {
    Dog().showName()
}
    
複製代碼

若是咱們調用 Dog 類的 showName(),它將輸出「Dog」,而不是「Cat」.

這樣是否是很不方便,因此擴展函數能夠重載相同名字可是不一樣簽名成員函數。例如:

class Dog {

    fun showName(){
        print("Dog")
    }
}


fun Dog.showName(name:String){
    print("Cat")
}
fun main(args: Array<String>) {
    Dog().showName("")
}
複製代碼

這樣咱們調用 showName("")函數,就會輸出 「Cat」了。

1.3 可空接受者

擴展函數能夠爲可空的接收者類型定義擴展。這樣的擴展能夠在對象變量上調用,即便其值爲 null,而且能夠在函數體內檢測 this == null ,這樣就能夠在沒有檢測 null 的時候調用成員函數了,列如:

fun Dog?.toString():String{
    if (this == null) return "null \n"
    return toString()
}

fun main(args: Array<String>) {
    var dog:Dog? = null
    print(dog.toString())
    dog  = Dog()
    print(dog.toString())
}

複製代碼

這段代碼 第一次打印輸出的是 "null \n" 第二次輸入的是改對象的地址。

2.擴展屬性

與函數相似,Kotlin也支持擴展屬性。 例如:

class Snake{
    var aaa = 1
}

var Snake.size:Int
    set(value) {aaa = value}
    get() = aaa +1

fun main(args: Array<String>) {
    val snake = Snake()
    print(snake.size)
    snake.size = 3
    print(snake.size)

}
複製代碼

如上例子所示。
**注意:**因爲擴展是沒有實際將變量成員插入類中,所以對擴展屬性來講幕後字段是無效的。因此擴展屬性不能有初始化器,只能顯示的提供 getters/setters 定義。

例如:

//錯誤:擴展屬性不能有初始化
val Snake.bbb = 1
複製代碼

如上例子是錯誤的。

3.伴生對象的擴展

咱們知道在kotlin中是沒有 static 這個關鍵字的,那咱們要怎麼實現靜態變量呢,那就用到了伴生對象,例如:

class Snake{
    var aaa = 1
    companion object {
        var Bbb = 1
    }
}
fun main(args: Array<String>) {
    //這樣就達到了Java中靜態變量同樣的效果了
    Snake.Bbb
}
複製代碼

那咱們怎麼對伴生對象進行擴展呢,其實也很簡單只是比其餘擴展中間加了一個 Companion 。如例:

fun Snake.Companion.foo(){...}
複製代碼

如上例,我就就定義了一個名爲 foo() 的擴展函數,那咱們這調用這個擴展函數了,其實和普通的擴展函數調用方法是同樣的,以下例:

Snake.foo()
複製代碼

示例地址:github.com/tao11122233…

相關文章
相關標籤/搜索