Kotlin委托屬性-簡(jiǎn)化數(shù)據(jù)訪(fǎng)問(wèn)

一榄鉴、概述

Kotlin有很多語(yǔ)法糖押袍,最近看了委托屬性, 用于筆者的開(kāi)源組件LightKV, 確實(shí)提高了不少易用性沦童。
關(guān)于LightKV,筆者在上一篇文章《LightKV-高性能key-value存儲(chǔ)組件》中有介紹LightKV的原理柜候,有興趣的讀者可以了解一下搞动。

LightKV的用法和SharePreferences類(lèi)似,都是key-value結(jié)構(gòu)渣刷,通過(guò)指定key讀寫(xiě)value鹦肿。
key-value 的 API 適用于存儲(chǔ)統(tǒng)計(jì),緩存辅柴,配置......等各種信息箩溃,
隨著APP的迭代,必然會(huì)有越來(lái)越多的信息需要存儲(chǔ)碌嘀,對(duì)應(yīng)用開(kāi)發(fā)而言涣旨,key-value的存儲(chǔ)不可或缺。

最初發(fā)布LightKV的時(shí)候股冗,有熱心網(wǎng)友提到:“想法很好霹陡,不過(guò)感覺(jué)用處不大,如果要存的數(shù)據(jù)很少那就sp …… ”
誠(chéng)然止状,SDK已經(jīng)提供了SharePreferences了烹棉,而且當(dāng)用SharePreferences還沒(méi)遇到性能瓶頸時(shí),也就沒(méi)有嘗試別的組件的的動(dòng)力了怯疤。

而且浆洗,之前的那一版(在引入委托屬性之前),只做到了“高效”集峦,沒(méi)有做到“易用”伏社。


二、舊版用法

public class AppData {
    private static final SyncKV DATA =
            new LightKV.Builder(GlobalConfig.getAppContext(), "app_data")
                    .logger(AppLogger.getInstance())
                    .executor(AsyncTask.THREAD_POOL_EXECUTOR)
                    .keys(Keys.class)
                    .encoder(new ConfuseEncoder())
                    .sync();

    // keys define
    public interface Keys {
        int SHOW_COUNT = 1 | DataType.INT;
        int ACCOUNT = 2 | DataType.STRING ;
        int TOKEN = 3 | DataType.STRING;
        int SECRET = 4 | DataType.ARRAY | DataType.ENCODE;
    }

    public static SyncKV data() {
        return DATA;
    }

    public static String getString(int key) {
        return DATA.getString(key);
    }

    public static void putString(int key, String value) {
        DATA.putString(key, value);
        DATA.commit();
    }

    public static byte[] getArray(int key) {
        return DATA.getArray(key);
    }

    public static void putArray(int key, byte[] value) {
        DATA.putArray(key, value);
        DATA.commit();
    }

    // ......
}
val account = AppData.getString(AppData.Keys.ACCOUNT)
if(TextUtils.isEmpty(account)){
      AppData.putString(AppData.Keys.ACCOUNT, "foo@gmail.com")
}

該用法的復(fù)雜度在于:
如果想用靜態(tài)方法(調(diào)用時(shí)簡(jiǎn)單一些)塔淤,則每一個(gè)數(shù)據(jù)存儲(chǔ)類(lèi)都需要實(shí)現(xiàn)一份各種類(lèi)型的get和set摘昌;
如果直接返回data()來(lái)讀寫(xiě), 寫(xiě)起來(lái)會(huì)比較長(zhǎng):

val account = AppData2.data().getString(Keys.ACCOUNT)
if(TextUtils.isEmpty(account)){
     AppData2.data().putString(Keys.ACCOUNT, "foo@gmail.com")
}

直到后來(lái)了解了Kotlin委托, 仿佛看到了曙光……


三、新版用法

object AppData : KVData() {
    override fun createInstance(): LightKV {
        return LightKV.Builder(GlobalConfig.appContext, "app_data")
                .logger(AppLogger)
                .executor(AsyncTask.THREAD_POOL_EXECUTOR)
                .encoder(GzipEncoder)
                .sync()
    }

    var showCount by int(1)
    var account by string(2)
    var token by string(3)
    var secret by array(4 or DataType.ENCODE)
}
val account = AppData.account
if (TextUtils.isEmpty(account)) {
   AppData.account = "foo@gmail.com"
}

使用Kotlin委托高蜂,省了各種put和set的方法調(diào)用第焰,看起來(lái)像是在直接訪(fǎng)問(wèn)AppData的屬性。


四妨马、屬性委托的實(shí)現(xiàn)

4.1 聲明屬性

語(yǔ)法: val/var <屬性名>: <類(lèi)型> by <表達(dá)式>挺举。

class Example {
    var p: String by Delegate()
}

by 后面的表達(dá)式是對(duì)應(yīng)的委托杀赢, 屬性的 get() 和 set() 會(huì)被委托給它的 getValue() 和 setValue() 方法。
當(dāng)然湘纵,如果聲明的是val, 則不會(huì)委托set()方法脂崔。

4.2 實(shí)現(xiàn)委托

屬性的委托,需要提供一個(gè) getValue() 函數(shù)和 setValue() 函數(shù)(如果聲明的是var 的話(huà))梧喷,并以operator修飾砌左。

class Delegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "$thisRef, thank you for delegating '${property.name}' to me!"
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("$value has been assigned to '${property.name}' in $thisRef.")
    }
}

例子中,thisRef 是 Example 的引用铺敌, 參數(shù) property 保存了對(duì)屬性p的描述汇歹,例如可以通過(guò)property.name獲取p的名字.

4.3 訪(fǎng)問(wèn)屬性

訪(fǎng)問(wèn) p 時(shí),將調(diào)用 Delegate 中的 getValue() 函數(shù);
給 p 賦值時(shí)偿凭,將調(diào)用 setValue() 函數(shù)产弹。

val e = Example()
println(e.p)
e.p = "NEW"

輸出結(jié)果:

Example@33a17727, thank you for delegating ‘p’ to me!
NEW has been assigned to ‘p’ in Example@33a17727.

4.4 屬性委托的原理

class C {
    var prop: Type by MyDelegate()
}
// 由編譯器生成的相應(yīng)代碼:
class C {
    private val prop$delegate = MyDelegate()
    var prop: Type
        get() = prop$delegate.getValue(this, this::prop)
        set(value: Type) = prop$delegate.setValue(this, this::prop, value)
}

前后對(duì)比,不難看出弯囊,其實(shí)屬性委托的本質(zhì)是“代理模式”的語(yǔ)法封裝痰哨。

五、優(yōu)化LightKV

5.1 定義抽象類(lèi)

abstract class KVData{
    internal var autoCommit = true

    abstract fun createInstance() : LightKV

    val data: LightKV by lazy {
        createInstance()
    }

    protected fun boolean(key: Int) = KVProperty<Boolean>(key or DataType.BOOLEAN)
    protected fun int(key: Int) = KVProperty<Int>(key or DataType.INT)
    protected fun float(key: Int) = KVProperty<Float>(key or DataType.FLOAT)
    protected fun double(key: Int) = KVProperty<Double>(key or DataType.DOUBLE)
    protected fun long(key: Int) = KVProperty<Long>(key or DataType.LONG)
    protected fun string(key: Int) = KVProperty<String>(key or DataType.STRING)
    protected fun array(key: Int) = KVProperty<ByteArray>(key or DataType.ARRAY)

    fun disableAutoCommit(){
        autoCommit = false
    }

    fun enableAutoCommit(){
        autoCommit = true
        data.commit()
    }
}

該抽象類(lèi)聲明了LightKV, 添加了自動(dòng)提交開(kāi)關(guān)匾嘱,以及定了個(gè)各種類(lèi)型委托斤斧。

5.2 實(shí)現(xiàn)委托

為方便編寫(xiě)委托, Kotlin標(biāo)準(zhǔn)庫(kù)定義了的ReadWriteProperty接口:

interface ReadWriteProperty<in R, T> {
    operator fun getValue(thisRef: R, property: KProperty<*>): T
    operator fun setValue(thisRef: R, property: KProperty<*>, value: T)
}

使用時(shí)實(shí)現(xiàn)接口的方法即可霎烙。
為了統(tǒng)一定義各個(gè)類(lèi)型委托撬讽,我們?cè)跇?gòu)造函數(shù)傳入key, 由key決定對(duì)應(yīng)的類(lèi)型操作。
通過(guò)thisRef.data(LightKV)和 key, 分別在getValue和setValue方法中實(shí)現(xiàn)取值和賦值悬垃。

class KVProperty<T>(private val key: Int) : ReadWriteProperty<KVData, T> {
    @Suppress("UNCHECKED_CAST", "IMPLICIT_CAST_TO_ANY")
    override operator fun getValue(thisRef: KVData, property: KProperty<*>): T
    = with(thisRef.data) {
        return when (key and DataType.MASK) {
            DataType.BOOLEAN -> getBoolean(key)
            DataType.INT -> getInt(key)
            DataType.FLOAT -> getFloat(key)
            DataType.LONG -> getLong(key)
            DataType.DOUBLE -> getDouble(key)
            DataType.STRING -> getString(key)
            DataType.ARRAY -> getArray(key)
            else -> throw IllegalArgumentException("Invalid Key: $key")
        } as T
    }

    override operator fun setValue(thisRef: KVData, property: KProperty<*>, value: T)  
    = with(thisRef.data) {
        when (key and DataType.MASK) {
            DataType.BOOLEAN -> putBoolean(key, value as Boolean)
            DataType.INT -> putInt(key, value as Int)
            DataType.FLOAT -> putFloat(key, value as Float)
            DataType.LONG -> putLong(key, value as Long)
            DataType.DOUBLE -> putDouble(key, value as Double)
            DataType.STRING -> putString(key, value as String)
            DataType.ARRAY -> putArray(key, value as ByteArray)
            else -> throw IllegalArgumentException("Invalid Key: $key")
        }
        if(mMode == LightKV.SYNC_MODE && thisRef.autoCommit){
            commit()
        }
    }
}

在LightKV為SYNC_MODE時(shí)自動(dòng)commit()游昼。
當(dāng)然,如果需要批量提交盗忱〗创玻可以調(diào)用disableAutoCommit()禁用自動(dòng)提交羊赵。

最后趟佃,在使用時(shí),繼承KVData昧捷,聲明屬性闲昭,即可像訪(fǎng)問(wèn)變量一樣讀寫(xiě)LightKV的數(shù)據(jù)(參見(jiàn)第三節(jié))。


六靡挥、下載

repositories {
    jcenter()
}

dependencies {
    implementation 'com.horizon.lightkv:lightkv:1.0.6'
}

項(xiàng)目地址:
https://github.com/No89757/LightKV

七序矩、結(jié)語(yǔ)

以前筆者對(duì)語(yǔ)法糖是不感興趣的,覺(jué)得語(yǔ)法糖掩蓋了細(xì)節(jié)跋破,容易使人“只知其然而不知其所以然”簸淀;
但是后來(lái)漸漸地也開(kāi)始接受了瓶蝴,技術(shù)的發(fā)展日新月異,不可能什么都從底層開(kāi)始構(gòu)筑租幕。
業(yè)界流傳有“人生苦短舷手,我用python”,說(shuō)的就是高級(jí)語(yǔ)言所帶來(lái)的便利劲绪,可以節(jié)約不少時(shí)間男窟。
當(dāng)然,C語(yǔ)言贾富,匯編語(yǔ)言歉眷,還是需要有人去寫(xiě),要看問(wèn)題領(lǐng)域颤枪。
對(duì)APP開(kāi)發(fā)而言汗捡,誠(chéng)然有大量的“搬磚”工作,磚頭搬累了汇鞭,來(lái)一發(fā)語(yǔ)法糖凉唐,也是不錯(cuò)的。


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末霍骄,一起剝皮案震驚了整個(gè)濱河市台囱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌读整,老刑警劉巖簿训,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異米间,居然都是意外死亡强品,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門(mén)屈糊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)的榛,“玉大人,你說(shuō)我怎么就攤上這事逻锐》蛏危” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵昧诱,是天一觀的道長(zhǎng)晓淀。 經(jīng)常有香客問(wèn)我,道長(zhǎng)盏档,這世上最難降的妖魔是什么凶掰? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上懦窘,老公的妹妹穿的比我還像新娘前翎。我一直安慰自己,他們只是感情好畅涂,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布鱼填。 她就那樣靜靜地躺著,像睡著了一般毅戈。 火紅的嫁衣襯著肌膚如雪苹丸。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,158評(píng)論 1 308
  • 那天苇经,我揣著相機(jī)與錄音赘理,去河邊找鬼。 笑死扇单,一個(gè)胖子當(dāng)著我的面吹牛商模,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蜘澜,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼施流,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了鄙信?” 一聲冷哼從身側(cè)響起瞪醋,我...
    開(kāi)封第一講書(shū)人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎装诡,沒(méi)想到半個(gè)月后银受,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鸦采,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年宾巍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片渔伯。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡顶霞,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出锣吼,到底是詐尸還是另有隱情选浑,我是刑警寧澤,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布吐限,位于F島的核電站鲜侥,受9級(jí)特大地震影響褂始,放射性物質(zhì)發(fā)生泄漏诸典。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望狐粱。 院中可真熱鬧舀寓,春花似錦、人聲如沸肌蜻。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蒋搜。三九已至篡撵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間豆挽,已是汗流浹背育谬。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留帮哈,地道東北人膛檀。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像娘侍,于是被迫代替她去往敵國(guó)和親咖刃。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 前言 人生苦多憾筏,快來(lái) Kotlin 嚎杨,快速學(xué)習(xí)Kotlin! 什么是Kotlin氧腰? Kotlin 是種靜態(tài)類(lèi)型編程...
    任半生囂狂閱讀 26,217評(píng)論 9 118
  • 本文是在學(xué)習(xí)和使用kotlin時(shí)的一些總結(jié)與體會(huì)磕潮,一些代碼示例來(lái)自于網(wǎng)絡(luò)或Kotlin官方文檔,持續(xù)更新... 對(duì)...
    竹塵居士閱讀 3,298評(píng)論 0 8
  • 寫(xiě)在開(kāi)頭:本人打算開(kāi)始寫(xiě)一個(gè)Kotlin系列的教程容贝,一是使自己記憶和理解的更加深刻自脯,二是可以分享給同樣想學(xué)習(xí)Kot...
    胡奚冰閱讀 838評(píng)論 1 1
  • 我們不再是同一節(jié)奏 你拒絕相見(jiàn)的請(qǐng)求 說(shuō)要學(xué)習(xí)工作研究 誰(shuí)知道是不是借口 我黯然神傷著轉(zhuǎn)頭 更加荒謬的是 我的時(shí)間...
    沈安樂(lè)閱讀 354評(píng)論 0 1
  • 我現(xiàn)在是一名超市收銀員,今天上晚班斤富,我正在服務(wù)臺(tái)里面和同事說(shuō)話(huà)膏潮。 正聊得開(kāi)心,我一轉(zhuǎn)頭看到一對(duì)來(lái)購(gòu)物的老夫妻在深情...
    孫丹丹86400閱讀 324評(píng)論 5 2