淺談Kotlin語法篇之擴展函數(五)

簡述: 今天帶來的是Kotlin淺談系列的第五彈,這講主要是講利用Kotlin中的擴展函數特性讓咱們的代碼變得更加簡單和整潔。擴展函數是Kotlin語言中獨有的新特性,利用它能夠減小不少的樣板代碼,大大提升開發的效率;此外擴展函數的使用也是很是簡單的。我會從如下幾個方面闡述Kotlin中的擴展函數。java

  • 一、爲何要使用Kotlin中的擴展函數?
  • 二、怎麼去使用擴展函數和擴展屬性?
  • 三、什麼是擴展函數和屬性?
  • 四、擴展函數和成員函數區別
  • 五、擴展函數不能夠被重寫

1、爲何要使用Kotlin中的擴展函數

咱們都知道在Koltin這門語言能夠與Java有很是好的互操做性,因此擴展函數這個新特性能夠很平滑與現有Java代碼集成。甚至純Kotlin的項目均可以基於Java庫,甚至Android中的一些框架庫,第三方庫來構建。擴展函數很是適合Kotlin和Java語言混合開發模式。在不少公司一些比較穩定良好的庫都是Java寫,也徹底不必去用Kotlin語言重寫。可是想要擴展庫的接口和功能,這時候擴展函數可能就會派上用場。使用Kotlin的擴展函數還有一個好處就是沒有反作用,不會對原有庫代碼或功能產生影響。先來看下擴展函數長啥樣app

  • 給TextView設置加粗簡單的例子
//擴展函數定義
fun TextView.isBold() = this.apply { 
	paint.isFakeBoldText = true
}

//擴展函數調用
activity.find<TextView>(R.id.course_comment_tv_score).isBold()
複製代碼

2、怎麼去使用擴展函數和擴展屬性

  • 一、擴展函數的基本使用

只須要把擴展的類或者接口名稱,放到即將要添加的函數名前面。這個類或者名稱就叫作接收者類型,類的名稱與函數之間用"."調用鏈接。this指代的就是接收者對象,它能夠訪問擴展的這個類可訪問的方法和屬性。框架

注意: 接收者類型是由擴展函數定義的,而接收者對象正是這個接收者類型的對象實例,那麼這個對象實例就能夠訪問這個類中成員方法和屬性,因此通常會把擴展函數當作成員函數來用。ide

  • 二、擴展屬性的基本使用 擴展屬性其實是提供一種方法來訪問屬性而已,而且這些擴展屬性是沒有任何的狀態的,由於不可能給現有Java庫中的對象額外添加屬性字段,只是使用簡潔語法相似直接操做屬性,實際上仍是方法的訪問。
//擴展屬性定義
var TextView.isBolder: Boolean
	get() {//必須定義get()方法,由於不能在現有對象添加字段,也天然就沒有了默認的get()實現
		return this.paint.isFakeBoldText
	}
	set(value) {
		this.paint.isFakeBoldText = value
	}
//擴展屬性調用
activity.find<TextView>(R.id.course_comment_tv_score).isBolder = true
複製代碼

注意:函數

  • 擴展屬性和擴展函數定義相似,也有接收者類型和接收者對象,接收者對象也是接收者類型的一個實例,通常能夠把它當作類中成員屬性來使用。測試

  • 必須定義get()方法,在Kotlin中類中的屬性都是默認添加get()方法的,可是因爲擴展屬性並非給現有庫中的類添加額外的屬性,天然就沒有默認get()方法實現之說。因此必須手動添加get()方法。ui

  • 因爲重寫了set()方法,說明這個屬性訪問權限是可讀和可寫,須要使用varthis

3、什麼是擴展函數和屬性

咱們從上面例子能夠看出,kotlin的擴展函數真是強大,能夠毫無反作用給原有庫的類增長屬性和方法,好比例子中TextView,咱們根本沒有去動TextView源碼,可是卻給它增長一個擴展屬性和函數。具備那麼強大功能,到底它背後原理是什麼?其實很簡單,經過decompile看下反編譯後對應的Java代碼就一目瞭然了。spa

  • 一、擴展函數實質原理

擴展函數實際上就是一個對應Java中的靜態函數,這個靜態函數參數爲接收者類型的對象,而後利用這個對象就能夠訪問這個類中的成員屬性和方法了,而且最後返回一個這個接收者類型對象自己。這樣在外部感受和使用類的成員函數是同樣的。翻譯

public final class ExtendsionTextViewKt {//這個類名就是頂層文件名+「Kt」後綴,這個知識上篇博客有詳細介紹
   @NotNull
   public static final TextView isBold(@NotNull TextView $receiver) {//擴展函數isBold對應其實是Java中的靜態函數,而且傳入一個接收者類型對象做爲參數
      Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
      $receiver.getPaint().setFakeBoldText(true);//設置加粗
      return $receiver;//最後返回這個接收者對象自身,以至於咱們在Kotlin中徹底可使用this替代接收者對象或者直接不寫。
   }
}
複製代碼
  • 二、Java中調用Kotlin中定義的擴展函數

分析完Kotlin中擴展函數的原理,咱們也就很清楚,如何在Java中去調用Kotlin中定義好的擴展函數了,實際上使用方法就是靜態函數調用,和咱們以前講的頂層函數在Java中調用相似,不過惟一不一樣是須要傳入一個接收者對象參數。

ExtendsionTextViewKt.isBold(activity.findViewById(R.id.course_comment_tv_score));//直接調用靜態函數
複製代碼
  • 三、擴展屬性實質原理

擴展屬性實際上就是提供某個屬性訪問的set,get方法,這兩個set,get方法是靜態函數,同時都會傳入一個接收者類型的對象,而後在其內部用這個對象實例去訪問和修改對象所對應的類的屬性。

public final class ExtendsionTextViewKt {
   //get()方法所對應生成靜態函數,而且傳入一個接收者類型對象做爲參數
   public static final boolean isBolder(@NotNull TextView $receiver) {
      Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
      return $receiver.getPaint().isFakeBoldText();
   }
   //set()方法所對應生成靜態函數,而且傳入一個接收者類型對象做爲參數和一個須要set的參數
   public static final void setBolder(@NotNull TextView $receiver, boolean value) {
      Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
      $receiver.getPaint().setFakeBoldText(true);
   }
}
複製代碼
  • 四、Java中調用Kotlin中定義的擴展屬性

Java調用Kotlin中定義的擴展屬性也很簡單,就至關於直接調用生成的set(),get()方法同樣。

ExtendsionTextViewKt.setBolder(activity.findViewById(R.id.course_comment_tv_score), true);
複製代碼

4、擴展函數和成員函數區別

說到擴展函數和成員函數的區別,經過上面例子咱們已經很清楚了,這裏作個概括總結:

  • 一、擴展函數和成員函數使用方式相似,能夠直接訪問被擴展類的方法和屬性。(原理: 傳入了一個擴展類的對象,內部其實是用實例對象去訪問擴展類的方法和屬性)
  • 二、擴展函數不能打破擴展類的封裝性,不能像成員函數同樣直接訪問內部私有函數和屬性。(原理: 原理很簡單,擴展函數訪問實際是類的對象訪問,因爲類的對象實例不能訪問內部私有函數和屬性,天然擴展函數也就不能訪問內部私有函數和屬性了)
  • 三、擴展函數其實是一個靜態函數是處於類的外部,而成員函數則是類的內部函數。
  • 四、父類成員函數能夠被子類重寫,而擴展函數則不行

5、擴展函數不能夠被重寫

在Kotlin和Java中咱們都知道類的成員函數是能夠被重寫的,子類是能夠重寫父類的成員函數,可是子類是不能夠重寫父類的擴展函數。

open class Animal {
    open fun shout() = println("animal is shout")//定義成員函數
}

class Cat: Animal() {
    override fun shout() {
        println("Cat is shout")//子類重寫父類成員函數
    }
}

//定義子類和父類擴展函數
fun Animal.eat() = println("Animal eat something")

fun Cat.eat()= println("Cat eat fish")

//測試
fun main(args: Array<String>) {
    val animal: Animal = Cat()
    println("成員函數測試: ${animal.shout()}")
    println("擴展函數測試: ${animal.eat()}")
}
複製代碼

運行結果:

以上運行結果再次說明了擴展函數並非類的一部分,它是聲明與類外部的,儘管子類和父類擁有了相同的擴展函數,可是實際上擴展函數是靜態函數。從編譯內部來看,子類和父類擁有了相同的擴展函數,實際上就是定義兩個同名的靜態擴展函數分別傳入父類對象和子類對象,那麼調用的方法確定也是父類中的方法和子類中的方法,因此輸出確定是父類的。

歡迎關注Kotlin開發者聯盟,這裏有最新Kotlin技術文章,每週會不按期翻譯一篇Kotlin國外技術文章。若是你也喜歡Kotlin,歡迎加入咱們~~~

相關文章
相關標籤/搜索