Kotlin-擴展(Extension)的能力邊界在哪?

1溪椎、擴展函數(shù)

我們對String定義一個擴展函數(shù)

//對String 增加擴展函數(shù)lastElement
//直接定義在kotlin文件里普舆,稱之為頂層擴展、
fun String.lastElement(): Char? {
    if (this.isEmpty()) {
        return null
    }
    return this[length - 1]
}
fun main() {
    val a = "Hello World"
    println(a.lastElement())
}

擴展函數(shù)我們定義在kotlin文件中校读,稱之為頂層擴展沼侣,任何地方都可以使用,轉成Java代碼看實現(xiàn)


public final class ExtentionKt {
   @Nullable
   public static final Character lastElement(@NotNull String $this$lastElement) {
      Intrinsics.checkNotNullParameter($this$lastElement, "$this$lastElement");
      CharSequence var1 = (CharSequence)$this$lastElement;
      boolean var2 = false;
      return var1.length() == 0 ? null : $this$lastElement.charAt($this$lastElement.length() - 1);
   }

   public static final void main() {
      String a = "Hello World";
      Character var1 = lastElement(a);
      boolean var2 = false;
      System.out.println(var1);
   }

}

其實實現(xiàn)很簡單歉秫,定義了一個在ExtentionKt 類中定義了一個lastElement靜態(tài)方法并傳入String對象蛾洛。在調用的時候,其實就是傳入了我們的字符串a即可端考。然后再算出最后一個字符雅潭。
所以 Kotlin 編譯器會將擴展函數(shù)轉換成對應的靜態(tài)方法,而擴展函數(shù)調用處的代碼也會被轉換成靜態(tài)方法的調用却特。

2扶供、擴展屬性

//對String 增加擴展函數(shù)lastElement
//直接定義在kotlin文件里,稱之為頂層擴展裂明、
fun String.lastElement(): Char? {
    if (this.isEmpty()) {
        return null
    }
    return this[length - 1]
}
//定義擴展屬性
val String.firstElement: Char?
    get() = if (isEmpty()) null else this[0]
fun main() {
    val a = "Hello World"
    println(a.lastElement())
    println(a.firstElement)

}

轉成Java代碼看實現(xiàn)

public final class ExtentionKt {
   @Nullable
   public static final Character lastElement(@NotNull String $this$lastElement) {
      Intrinsics.checkNotNullParameter($this$lastElement, "$this$lastElement");
      CharSequence var1 = (CharSequence)$this$lastElement;
      boolean var2 = false;
      return var1.length() == 0 ? null : $this$lastElement.charAt($this$lastElement.length() - 1);
   }

   @Nullable
   public static final Character getFirstElement(@NotNull String $this$firstElement) {
      Intrinsics.checkNotNullParameter($this$firstElement, "$this$firstElement");
      CharSequence var1 = (CharSequence)$this$firstElement;
      boolean var2 = false;
      return var1.length() == 0 ? null : $this$firstElement.charAt(0);
   }

   public static final void main() {
      String a = "Hello World";
      Character var1 = lastElement(a);
      boolean var2 = false;
      System.out.println(var1);
      var1 = getFirstElement(a);
      var2 = false;
      System.out.println(var1);
   }
   // $FF: synthetic method
   public static void main(String[] var0) {
      main();
   }
}

從Java代碼中我們可以看到椿浓,最終也是轉成一個靜態(tài)的getFirstElement方法,其實我們很容易理解闽晦,當我們的屬性firstElement 設置成val 非私有的時候扳碍,對應的Java代碼就會默認持有getter方法,也就是我們的getFirstElement仙蛉。當我們定義了擴展屬性笋敞,那么對應的Java代碼就會轉成相應的靜態(tài)的getter方法。

3荠瘪、擴展的能力邊界夯巷?

從以上我們轉成Java便可以知道,kotlin中不管的是擴展函數(shù)還是擴展屬性對應都是Java中的靜態(tài)方法哀墓,也就是主要用于替代Java 中的各種工具類趁餐。例如我們的工具類主要是操作字符串,那么我們就對String類進行擴展

擴展能做什么篮绰?

在Kotlin中幾乎所有的類都可以擴展后雷、主要用途是取代Java中的各種工具類,StringUtils等等。當我們對某個類進行擴展成員的時候臀突,擴展的成員實際上不是真正的成員勉抓,但是我們在編譯器中有智能的提示,這樣一來就方便開發(fā)候学。

擴展不能做什么琳状?

  • 擴展不是真正的成員,無法被子類重寫
  • 擴展屬性無法存儲狀態(tài)
    如:
//定義擴展屬性
val String.firstElement: Char?
    get() = if (isEmpty()) null else this[0]

其實很容易理解盒齿,擴展屬性轉成Java代碼其實就對應了靜態(tài)的getter方法而已,具體的值由getter方法返回值決定

  • 擴展的訪問作用域
    (1)定義處的成員
    (2)接受者類型的公開成員
    如:
private val name = "dog";
//定義擴展屬性
val String.firstElement: Char?
    get() = if (isEmpty()) {
        null
    } else {
        println(name)
        println(this.length)
        this[0]
    }

name成員雖然是私有的困食,但是定義在和擴展屬性firstElement同一個文件边翁,因此是可以訪問到name 的,否則不能
其次就是訪問被擴展類型的公開成員硕盹。如以上的例子訪問String的length 通過this.length符匾,如果length是私有的則不能訪問。
以上我們定義的是頂層擴展瘩例,如果我們在某個類中進行擴展呢啊胶?

class Pig {
    //對String 增加擴展函數(shù)lastElement
//直接定義在kotlin文件里,稱之為頂層擴展垛贤、
    fun String.lastElement(): Char? {
        if (this.isEmpty()) {
            return null
        }
        return this[length - 1]
    }

    private val name = "dog";
    //定義擴展屬性
    val String.firstElement: Char?
        get() = if (isEmpty()) {
            null
        } else {
            println(name)
            println(this.length)
            this[0]
        }

    fun testDemo(){
        val a = "Hello World"
        println(a.lastElement())
        println(a.firstElement)
    }
}

轉成Java代碼


public final class Pig {
   private final String name = "dog";

   @Nullable
   public final Character lastElement(@NotNull String $this$lastElement) {
      Intrinsics.checkParameterIsNotNull($this$lastElement, "$this$lastElement");
      CharSequence var2 = (CharSequence)$this$lastElement;
      boolean var3 = false;
      return var2.length() == 0 ? null : $this$lastElement.charAt($this$lastElement.length() - 1);
   }

   @Nullable
   public final Character getFirstElement(@NotNull String $this$firstElement) {
      Intrinsics.checkParameterIsNotNull($this$firstElement, "$this$firstElement");
      CharSequence var2 = (CharSequence)$this$firstElement;
      boolean var3 = false;
      Character var10000;
      if (var2.length() == 0) {
         var10000 = null;
      } else {
         String var4 = this.name;
         var3 = false;
         System.out.println(var4);
         int var5 = $this$firstElement.length();
         var3 = false;
         System.out.println(var5);
         var10000 = $this$firstElement.charAt(0);
      }

      return var10000;
   }

   public final void testDemo() {
      String a = "Hello World";
      Character var2 = this.lastElement(a);
      boolean var3 = false;
      System.out.println(var2);
      var2 = this.getFirstElement(a);
      var3 = false;
      System.out.println(var2);
   }
}

其實非常容易了理解焰坪,當擴展成員定義在類中,那么只能在類中訪問
因此可知:
如果是頂層擴展聘惦,是可以被全局使用的某饰,擴展成員的訪問作用域限于所在文件的所有成員,以及被擴展類型的公開成員
如果是在類中擴展善绎,那么只能在類中使用黔漂,擴展成員的訪問作用域為該類的成員,以及被擴展類型的公開成員

4禀酱、實戰(zhàn)與思考

以上我們分析了擴展函數(shù)常用于替代Java中的各種工具類炬守,僅此而已嗎?
我們來看下Kotlin中的String

public class String : Comparable<String>, CharSequence {
    companion object {}
    
    public override fun get(index: Int): Char

    public override fun subSequence(startIndex: Int, endIndex: Int): CharSequence

    public override fun compareTo(other: String): Int
}

我們看到String中只有幾個核心方法剂跟,而平時我們使用到的這么多API减途,都在Strings這個頂層擴展文件中,


image.png

由此可知浩聋,在優(yōu)化我們的項目架構的時候观蜗,我們可以將核心方法寫在類中,非核心方法我們可以通過擴展的方式實現(xiàn)分離衣洁。

第二就是對于SDK中的代碼墓捻,我們在使用的時候,總是要寫很多公共的模板代碼,如果我們使用Java可以想到把他封裝成一個靜態(tài)工具類砖第,在Kotlin我們便可以通過擴展成員的方式進行封裝撤卢。雖然說擴展的方式轉換成Java代碼也是通過靜態(tài)方法進行封裝,但是對于我們開發(fā)中卻非常方便梧兼,開發(fā)中編譯器就類似把擴展成員當做真正的成員放吩,智能提示。
如下代碼:

當我們要設置View的margin的時候羽杰,總要寫很多模板代碼

    val param = textView?.layoutParams as ViewGroup.MarginLayoutParams
    param.bottomMargin = 10
    param.topMargin = 10
    param.marginStart = 10
    param.marginEnd = 10
    textView.layoutParams = param

那么我們便可以通過擴展的方式渡紫,使得代碼更加方便

fun View.setMargin(left: Int, top: Int, right: Int, bottom: Int) {
    ( layoutParams as ViewGroup.MarginLayoutParams).let { 
        it.bottomMargin = bottom
        it.topMargin = top
        it.marginStart = left
        it.marginEnd = right
    }
}
fun main() {
   val textView: TextView? = null
    textView?.setMargin(10, 20, 30, 50)

    val button: Button? = null
    button?.setMargin(10, 20, 30, 50)
}

這樣針對所有的View我們都可以使用。

5考赛、小結

  • 擴展我們可以用于替代Java中的一些工具類
  • 我們可以將核心方法寫在類中惕澎,非核心成員寫在擴展成員中
  • 當我們調用一些外部SDK中的函數(shù)總是要寫很多模板方法,我們便可以對SDK類進行擴展
  • 擴展成員不是真正的成員颜骤,因此不能繼承唧喉,不能保存數(shù)據(jù),其底層最終也是封裝成了靜態(tài)的方式實現(xiàn)
  • 頂層擴展在任何地方都可以使用忍抽,類中擴展只能在類中使用八孝,因此我們一般使用底層擴展
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市鸠项,隨后出現(xiàn)的幾起案子干跛,更是在濱河造成了極大的恐慌,老刑警劉巖祟绊,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件驯鳖,死亡現(xiàn)場離奇詭異,居然都是意外死亡久免,警方通過查閱死者的電腦和手機浅辙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來阎姥,“玉大人记舆,你說我怎么就攤上這事『舭停” “怎么了泽腮?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長衣赶。 經(jīng)常有香客問我诊赊,道長,這世上最難降的妖魔是什么府瞄? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任碧磅,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘鲸郊。我一直安慰自己丰榴,他們只是感情好,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布秆撮。 她就那樣靜靜地躺著四濒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪职辨。 梳的紋絲不亂的頭發(fā)上盗蟆,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天,我揣著相機與錄音舒裤,去河邊找鬼姆涩。 笑死,一個胖子當著我的面吹牛惭每,可吹牛的內容都是我干的。 我是一名探鬼主播亏栈,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼台腥,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了绒北?” 一聲冷哼從身側響起黎侈,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎闷游,沒想到半個月后峻汉,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡脐往,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年休吠,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片业簿。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡瘤礁,死狀恐怖,靈堂內的尸體忽然破棺而出梅尤,到底是詐尸還是另有隱情柜思,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布巷燥,位于F島的核電站赡盘,受9級特大地震影響,放射性物質發(fā)生泄漏缰揪。R本人自食惡果不足惜陨享,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧霉咨,春花似錦蛙紫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至喷斋,卻和暖如春唁毒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背星爪。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工浆西, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人顽腾。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓近零,卻偏偏與公主長得像,于是被迫代替她去往敵國和親抄肖。 傳聞我的和親對象是個殘疾皇子久信,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內容

  • 一、前言 Kotlin中的擴展函數(shù)特性讓我們的代碼變得更加簡單和整潔漓摩。擴展函數(shù)是Kotlin語言中獨有的新特性裙士,利...
    青葉小小閱讀 1,278評論 0 1
  • 概述 Kotlin是面向對象的靜態(tài)類型語言; 在Kotlin中管毙,所有東西都是對象腿椎,在這個意義上可以在任意變量上調用...
    CodeMagic閱讀 382評論 0 0
  • 擴展函數(shù)申明 接收者類型(recievier type)通常是類或接口的名稱做前綴。擴展函數(shù)體內的this代表的是...
    嘎嘎zz閱讀 1,158評論 0 0
  • 擴展 擴展方法 Kotlin支持擴展方法和擴展屬性夭咬。語法:被擴展的類/接口名.方法名() 父類不能使用子類的擴展方...
    AilurusFulgens閱讀 288評論 0 1
  • 從零開始學Kotlin基礎篇系列文章 什么是擴展函數(shù) 擴展函數(shù)數(shù)是指在一個類上增加一種新的行為啃炸,我們甚至沒有這個類...
    SiberianDante閱讀 342評論 0 1