项目应用中一般会对 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“]
}
}
复制代码