項目應用中一般會對 SharedPreference 再封裝一層,使用 Kotlin 語法糖能夠極大簡化這層封裝。本文將使用委託、泛型、重載運算符來達到化簡的目的。java
這是 Kotlin 系列的第八篇,系列文章目錄以下:bash
SharedPreference 在項目中的封裝類一般以下所示:
public final class SPUtils {
//'持有SharedPreferences實例'
private SharedPreferences sp;
//'在構造函數中構建SharedPreferences實例'
private SPUtils(final String spName) {
sp = Utils.getApp().getSharedPreferences(spName, Context.MODE_PRIVATE);
}
//'寫函數'
public void put(@NonNull final String key, final int value, final boolean isCommit) {
if (isCommit) {
sp.edit().putInt(key, value).commit();
} else {
sp.edit().putInt(key, value).apply();
}
}
//'讀函數'
public int getInt(@NonNull final String key, final int defaultValue) {
return sp.getInt(key, defaultValue);
}
...
}
複製代碼
SharedPreferences 共支持 6 種數據類型的讀寫,加起來一共 12 個讀寫函數。
用 Kotlin類委託語法,能夠將 SharedPreferences 工具類聲明以下:
//'將類委託給 SharedPreferences 實例'
class Preference(private val sp: SharedPreferences) : SharedPreferences by sp {
...
}
複製代碼
Preference
繼承接口SharedPreferences
,這樣作的目的是對業務層保留SharedPreferences
原有接口。並把構建SharedPreferences
實例移出工具類,交給業務層處理。
class Class2(private val c1: Class1) : Class1 by c1
這個語法叫類委託,它的意思是:Class2
是Class1
的子類型而且Class2
將全部接口的實現委託爲c1
實例,這樣 Kotlin 會自動生成全部接口並將其實現委託給超類型的實例。這就是爲啥Preference
並未實現接口中的任何一個函數,編譯器卻不報錯,打開 Kotlin 字節碼驗證一下:
public final class Preference implements SharedPreferences {
private final SharedPreferences sp;
public Preference(@NotNull SharedPreferences sp) {
Intrinsics.checkParameterIsNotNull(sp, ‘sp’);
super();
//'依賴注入'
this.sp = sp;
}
public boolean getBoolean(String key, boolean defValue) {
//'將實現委託爲sp實例'
return this.sp.getBoolean(key, defValue);
}
public float getFloat(String key, float defValue) {
//'將實現委託爲sp實例'
return this.sp.getFloat(key, defValue);
}
...
}
複製代碼
Kotlin 自定將全部接口的實現委託給了 SharedPreferences 實例。by
關鍵詞使咱們能夠省去這些模版代碼。
爲了簡化讀寫函數的調用,從新定義了兩個函數:
class Preference(private val sp: SharedPreferences) : SharedPreferences by sp {
//'寫函數'
operator fun set(key: String, isCommit: Boolean = false , value: Int) {
val edit = sp.edit()
edit.putInt(key,value)
if (isCommit) {
edit.commit()
} else {
edit.apply()
}
}
//'讀函數'
operator fun get(key: String, default: Int): Int = sp.getInt(key,default)
}
複製代碼
這兩個函數都以保留詞operator
開頭,它表示重載運算符,即從新定義運算符的語義。Kotlin 中預約義了一些函數名和運算符的對應關係,稱爲約定:
函數名 | 運算符 |
---|---|
plus | a + b |
times | a * b |
div | a / b |
mod | a % b |
minus | a - b |
unaryPlus | + a |
unaryMinus | - a |
not | ! a |
inc | ++a , a++ |
dec | --a, a-- |
get | a[b] |
set | a = b |
rangeTo | .. |
此次用到的是約定是get
和set
,聲明他們時能夠定義任意長度的參數,分別以 2 參數和 3 參數爲例:
java | kotlin |
---|---|
p.get( key, default ) | p[ key, default ] |
p.set( key, true, value ) | p[ key, true ] = value |
其中set
函數中最後一個參數是=
右邊的值,其他參數是=
左邊的值。
而後就能夠像這樣簡潔地讀寫了:
class Activity1 : AppCompatActivity() {
private lateinit var pre: Preference
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//'構建Preferences實例'
pre = Preference(getSharedPreferences(」test「, Context.MODE_PRIVATE))
//'寫'
pre[」a「] = 1
//'讀'
pre[」a「,0]
}
}
複製代碼
但只定義了 Int 值的讀寫,難道其他的類型都要新起一對讀寫函數?
泛型是類型的類型,使用泛型就能夠讓讀寫函數與具體類型解耦:
class Preference(private val sp: SharedPreferences) : SharedPreferences by sp {
//'將寫函數的數據類型抽象爲T'
operator fun <T> set(key: String, isCommit: Boolean = false , value: T) {
with(sp.edit()) {
//'將泛型具體化,並委託爲sp實例'
when (value) {
is Long -> putLong(key, value)
is Int -> putInt(key, value)
is Boolean -> putBoolean(key, value)
is Float -> putFloat(key, value)
is String -> putString(key, value)
is Set<*> -> (value as? Set<String>)?.let { putStringSet(key, it) }
else -> throw IllegalArgumentException(」unsupported type of value「)
}
if (isCommit) {
commit()
} else {
apply()
}
}
}
//'將讀函數的數據類型抽象爲T'
operator fun <T> get(key: String, default: T): T = with(sp) {
//'將泛型具體化,並委託爲sp實例'
when (default) {
is Long -> getLong(key, default)
is Int -> getInt(key, default)
is Boolean -> getBoolean(key, default)
is Float -> getFloat(key, default)
is String -> getString(key, default)
is Set<*> -> getStringSet(key, mutableSetOf())
else -> throw IllegalArgumentException(」unsupported type of value「)
} as T
}
}
複製代碼
其中的with
、as
、when
、is
的詳細解釋能夠翻閱該系列前面的文章。
而後就能夠像這樣無視類型地使用讀寫函數了:
class DelegateActivity : AppCompatActivity() {
private lateinit var pre: Preference
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
pre = Preference(getSharedPreferences(」test「, Context.MODE_PRIVATE))
//'寫int'
pre[」a「] = 1
//'使用commit方式寫String'
pre[」b「,true] = 」2「
//'寫Set<String>'
pre[」c「] = mutableSetOf(」cc「,「dd」)
//'讀String'
pre[」b「]
}
}
複製代碼