Kotlin中理解委托屬性,并自定義委托

常用的by lazy 延遲委托 , by Delegates.observable() 可觀察屬性委托, by Delegates.nonNull()等等

語法是: val/var<屬性名>:<類型> by<表達式>贫途。在 by后面的表達式是該委托

屬性對應的get() (和set())會被委托給它的getValue()和setValue()方法藕施。
所以,kotlin中的代理僅僅是代理了get 和 set 兩個方法

屬性的委托不必實現(xiàn)任何的接口,但是需要重載操作符getValue()函數(shù)(和setValue()——對于var屬性)
但是對于val可以實現(xiàn)ReadOnlyProperty,var實現(xiàn)ReadWriteProperty接口(就是幫你實現(xiàn)兩個需要重載的操作符的接口)來更快的進行自定義委托.

//此段代碼為ReadWriteProperty的接口
public interface ReadWriteProperty<in R, T> {
    public operator fun getValue(thisRef: R, property: KProperty<*>): T
    public operator fun setValue(thisRef: R, property: KProperty<*>, value: T)
}

那么他們究竟是怎么工作的呢?
我們來看看nonNull()委托的代碼吧
我初始化了一個Int對象,且用委托表示int1不可以為空
var int1 : Int by Delegates.notNull()
那么我們進入notNull()的代碼里查看他的實現(xiàn)
public fun <T: Any> notNull(): ReadWriteProperty<Any?, T> = NotNullVar()
很明顯,代理給一個ReadWriteProperty類了
那么NotNullVar()是什么呢

private class NotNullVar<T: Any>() : ReadWriteProperty<Any?, T> {
    private var value: T? = null

    public override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return value ?: throw IllegalStateException("Property ${property.name} should be initialized before get.")
    }

    public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        this.value = value
    }
}

我來解釋一下,當給int1復制的時候,就會自動調用上面的setValue(),其中的三個參數(shù)
第一個thisRef表示持有該對象的對象,即該int1的所有者.
第二個參數(shù) property 是該值的類型,
第三個參數(shù) value 就是屬性的值了
在第一次調用setvalue的時候,將該值存到value中,getvalue的時候對value進行是否為空的判斷.空就拋異常,這就完成了nonNull的委托


類似的我們看看by Delegates.observable() 這個委托
先看看該委托的使用方法吧

    var int2 : Int by Delegates.observable(1){
        property, oldValue, newValue ->
        //oldvalue是修改前的值,newValue是修改后的值
    }

該委托的實現(xiàn)

    public inline fun <T> observable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Unit):
        ReadWriteProperty<Any?, T> = object : ObservableProperty<T>(initialValue) {
            override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) = onChange(property, oldValue, newValue)
        }

最終委托到的對象,注意這是一個抽象類

public abstract class ObservableProperty<T>(initialValue: T) : ReadWriteProperty<Any?, T> {
    private var value = initialValue

    protected open fun beforeChange(property: KProperty<*>, oldValue: T, newValue: T): Boolean = true


    protected open fun afterChange (property: KProperty<*>, oldValue: T, newValue: T): Unit {}

    public override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return value
    }

    public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        val oldValue = this.value //修改前的值

        //beforeChange一直返回true 不知此段代碼意義何在
        if (!beforeChange(property, oldValue, value)) {
            return
        }
        this.value = value //修改后的值
        //調用上一段代碼中重寫的方法
        afterChange(property, oldValue, value)
    }
}

很明顯,在setValue的過程中,調用了afterChange(),而afterChange是你使用該代理的時候傳入的lambda,所以每次修改該對象的值的時候,都會調用你傳入的函數(shù),實現(xiàn)了對對象的值改變的觀察.


看完上面兩個例子,我們來自定義一個委托用來實現(xiàn)finViewById吧
當然例子很簡陋,只能在activity中使用,僅供參考


class MainActivity : AppCompatActivity() ,MainContract.View {

    val imageView : ImageView by bindView(R.id.imageView)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        imageView.setImageDrawable(null)
    }
}

fun <T: View> bindView( id : Int): FindView<T> = FindView(id)

class FindView<T : View >(val id:Int) : ReadOnlyProperty<Any?,T>{

    override fun getValue(thisRef: Any?, property: KProperty<*>): T {

        if(this.value == null) {
            this.value = (thisRef as Activity).findViewById<T>(id)
        }
        return this.value?:throw RuntimeException("can not find this view")
    }

    var value : T? = null
}

但是,為什么thisref 可以強轉成activity呢?上面已經說了thisRef是該對象的持有者,在上面的代碼中,即MainActivity.

我們將kotlin代碼轉成字節(jié)碼再轉到java代碼中查看


//反射得到該類的屬性
static final KProperty[] $$delegatedProperties = 
new KProperty[]{(KProperty)Reflection.property1(new PropertyReference1Impl(
        Reflection.getOrCreateKotlinClass(MainActivity.class), 
                "imageView", "getImageView()Landroid/widget/ImageView;"))};

  //真正的代理類
   @NotNull
   private final FindView imageView$delegate = MainActivityKt.bindView(2131230801);


  //通過代理類得到imageview
   @NotNull
   public final ImageView getImageView() {
      //在這里,我們就一清二楚的知道了getValue傳入的兩個參數(shù)究竟是什么了!!
      return (ImageView)this.imageView$delegate.getValue(this, $$delegatedProperties[0]);
   }

   protected void onCreate(@Nullable Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      this.setContentView(2131361819);
      //通過代理類得到imageview
      this.getImageView().setImageDrawable((Drawable)null);
   }

代理類的實現(xiàn)

public final class FindView implements ReadOnlyProperty {
   @Nullable
   private View value;
   private final int id;

   @NotNull
   public View getValue(@Nullable Object thisRef, @NotNull KProperty property) {
      Intrinsics.checkParameterIsNotNull(property, "property");
      if(this.value == null) {
         if(thisRef == null) {
            throw new TypeCastException("null cannot be cast to non-null type android.app.Activity");
         }

         this.value = ((Activity)thisRef).findViewById(this.id);
      }

      View var10000 = this.value;
      if(this.value != null) {
         return var10000;
      } else {
         throw (Throwable)(new RuntimeException("can not find this view"));
      }
   }

   // $FF: synthetic method
   // $FF: bridge method
   public Object getValue(Object var1, KProperty var2) {
      return this.getValue(var1, var2);
   }

   @Nullable
   public final View getValue() {
      return this.value;
   }

   public final void setValue(@Nullable View var1) {
      this.value = var1;
   }

   public final int getId() {
      return this.id;
   }

   public FindView(int id) {
      this.id = id;
   }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末熔吗,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子赚爵,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異癞志,居然都是意外死亡,警方通過查閱死者的電腦和手機框产,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進店門今阳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人茅信,你說我怎么就攤上這事∧钩簦” “怎么了蘸鲸?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長窿锉。 經常有香客問我酌摇,道長,這世上最難降的妖魔是什么嗡载? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任窑多,我火速辦了婚禮,結果婚禮上洼滚,老公的妹妹穿的比我還像新娘埂息。我一直安慰自己,他們只是感情好遥巴,可當我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布千康。 她就那樣靜靜地躺著,像睡著了一般铲掐。 火紅的嫁衣襯著肌膚如雪拾弃。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天摆霉,我揣著相機與錄音豪椿,去河邊找鬼。 笑死携栋,一個胖子當著我的面吹牛搭盾,可吹牛的內容都是我干的。 我是一名探鬼主播刻两,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼增蹭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了磅摹?” 一聲冷哼從身側響起滋迈,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤霎奢,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后饼灿,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體幕侠,經...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年碍彭,在試婚紗的時候發(fā)現(xiàn)自己被綠了晤硕。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡庇忌,死狀恐怖舞箍,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情皆疹,我是刑警寧澤疏橄,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站略就,受9級特大地震影響捎迫,放射性物質發(fā)生泄漏。R本人自食惡果不足惜表牢,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一窄绒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧崔兴,春花似錦彰导、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至折汞,卻和暖如春倔幼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背爽待。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工损同, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鸟款。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓膏燃,卻偏偏與公主長得像,于是被迫代替她去往敵國和親何什。 傳聞我的和親對象是個殘疾皇子组哩,可洞房花燭夜當晚...
    茶點故事閱讀 43,472評論 2 348

推薦閱讀更多精彩內容

  • 簡述 在java中一些屬性的具有相同的行為怎么辦,抽象出類然后再去依賴調用,而在Kotlin中只需要一個by關鍵字...
    i校長閱讀 2,619評論 0 4
  • Kotlin 知識梳理系列文章 Kotlin 知識梳理(1) - Kotlin 基礎Kotlin 知識梳理(2) ...
    澤毛閱讀 2,649評論 0 6
  • 晨起澆水時與綠葉私語 告訴它昨夜的夢 洗漱時對鏡子擠眉弄眼 閉眼給今天的自己笑容 做早餐的功夫也能隨音樂擺動 看花...
    白晚安閱讀 235評論 5 4
  • 6伶贰、在一段時間內蛛砰,心理學家對一個影響世界上少數(shù)居民的現(xiàn)象引起了關注。當他發(fā)生時黍衙,挪威人稱之為“黑暗時期”每...
    鄧潔兒閱讀 134評論 0 1
  • 晚上十一點泥畅,困意襲卷,眼皮開始自覺的耷拉下來琅翻,再精彩的文章都已看不下去了位仁,可是腦海里有一個粗重的聲音在呼喊:說好今...
    堯月之秀閱讀 616評論 8 5