写在前面
本文上接:Kotlin进阶学习2,在上次的文章里,我们学习了高阶函数。这次我们就来学习如何利用高阶函数简化Android中的各种常见操作,并且学习一下泛型的内容。
使用高阶函数简化Android开发
既然谷歌一直推荐使用Kotlin作为安卓开发的语言,那么谷歌肯定为我们提供了各种方便使用Kotlin开发的库。接下来我们就尝试自己实现其中的一些功能,以加固高阶函数的理解。
简化SharedPreferences的用法
在学习如何简化之前,先来看看SharedPreferences的基本用法:
- 调用SharedPreferences的edit()方法获得SharedPreferences.Editor对象
- 向SharedPreferences.Editor()对象中添加数据
- 调用apply方法将添加的数据提交,完成数据存储。
在了解了基本用法后,我们就可以尝试用高阶函数来改写了:
fun SharedPreferences.open(block:SharedPreferences.Editor.() -> Unit){
val editor = edit()
editor.block()
editor.apply()
}
这段代码不是很难理解。首先我们通过扩展函数向SharedPreferences类中添加了一个open函数,且接收了一个函数类型的参数。由于在函数体内有了SharedPreferences的上下文,所以可以直接调用edit()方法获取SharedPreferences.Editor对象,最后调用我们传入的block()函数,提交即可。
那么我们直接来使用一下吧:
getSharedPreferences("data",Context.MODE_PRIVATE).open{
putString("name","Tom")
putInt("age",28)
}
因为这里的open函数里已经有了SharedPreferences.Editor的上下文了,所以可以直接调用一系列的put方法。
当然,谷歌提供的KTX扩展库已经包含了上述的功能,且我们用kotlin创建项目时已经自动导入了该依赖:
getSharedPreferences("data",Context.MODE_PRIVATE).edit{
putString("name","Tom")
putInt("age",28)
}
可以看到,除了函数名变成了edit之外,没有什么不同的地方。
简化ContentValues的用法
ContentValues的用法想必大家也不会陌生。主要用于在操作Sqlite数据库的存储和修改方法。具体用法这里就不再演示了,想必大家都知道。
在开始之前,先补充一个小的知识点:在kotlin中使用A to B 这样的语法结构会创建一个Pair对象。具体的知识点我们将在下面补充。有了这个知识前提后,我们先来定义一个方法:
fun cvOf(vararg pairs:Pair<String,Any?>):ContentValues{
}
这里的定义出现了很多新的词汇。首先是vararg,其实就是java里的可变参数列表。意思为我们允许向这个方法传入n个Pair类型的参数,这些参数都会赋值到这个pairs变量上,我们通过for循环就可以解析出来了。之后是Pair类型,Pair类型是一个键值对结构,比较值得庆幸的是ContentValues的key都是字符串类型,我们直接写String就可以了。但value却有很多种,这里的Any?中的Any是Kotlin中所有类的共同基类,类似Java中的Object类,问号表示允许传入空值。
接下来就是逻辑部分:
fun cvOf(vararg pairs:Pair<String,Any?>):ContentValues{
val cv = ContentValues()
for(pair in pairs){
val key = pair.first
val value = pair.second
when(value){
is Int -> cv.put(key,value)
is Long -> cv.put(key,value)
is Short -> cv.put(key,value)
is Float -> cv.put(key,value)
is Double -> cv.put(key,value)
is Boolean -> cv.put(key,value)
is String -> cv.put(key,value)
is Byte -> cv.put(key,value)
is ByteArray -> cv.put(key,value)
null -> cv.putNull(key)
}
}
}
核心思路很简单,构建一个ContentValues()对象,然后循环遍历pairs,根据类型依次将数据put进去。需要注意的是,这里我们没有强转,因为这里使用了Koltin中的Smart Cast功能,比如when语句进入到Int条件分支后,value就会自动被转换成Int型,不再需要我们转换类型了。
有了cvOf()函数后,我们直接来使用一下:
val values = cvOf("name" to "Thrones","author" to "ee","pages" to 20,"price" to 20.85)
db.insert("Book",null,values)
当然,这里的cvOf()已经很好用了,但和高阶函数似乎没有啥关系。我们可以再优化一下:
fun cvOf(vararg pairs:Pair<String,Any?>) = ContentValue().apply{
for(pair in pairs){
val key = pair.first
val value = pair.second
when(value){
is Int -> put(key,value)
is Long -> put(key,value)
is Short ->put(key,value)
is Float -> put(key,value)
is Double -> put(key,value)
is Boolean -> put(key,value)
is String -> put(key,value)
is Byte -> put(key,value)
is ByteArray -> put(key,value)
null -> putNull(key)
}
}
}
这样,不仅代码更加优雅,也充分运用了我们学习到的高阶函数。当然,KTX库也提供了类似的方法,函数名叫contentValuesOf,使用方法是一样的。
泛型入门
介绍
泛型大家一定不会陌生,Java早就引入了泛型的机制,Kotlin更是很早就支持了泛型。篇幅原因,这里就先只学习一下泛型的基本用法。那什么是泛型呢?泛型其实就是允许我们在不指定具体类型的情况下进行编程,极大地方便了我们的开发和拓展。
泛型主要有两种定义方式,泛型类和泛型方法。使用的语法结构都是
泛型类
要定义一个泛型类,如下:
class MyClass<T>{
fun method (param:T) :T{
return param
}
}
可以看到,很简单也很方便。我们要使用的时候,直接这样就可以了:
val myClass = Myclass<Int>()
val result = myClass.method(123)
泛型方法
如果我们不想定义一个泛型类,可以直接定义一个泛型方法:
class myClass{
fun <T> method(param:T):T{
return param
}
}
可以看到,我们只是把泛型声明写到了方法上而已。使用方法也就进行了改变:
val myClass = MyClass()
val result = myClass.method<Int>(123)
其中,Kotlin还允许我们对泛型的类型进行限制,如果我们不想让泛型的类型指定为任意类型的话,可以规定他的上界:
class myClass{
fun <T : Number> method(param:T):T{
return param
}
}
这里意思是说,我们的泛型只能输入数字类型的,如果指定成其他类型肯定会报错。
另外,默认情况下所有泛型都是可以指定为可空类型的。因为默认上界是Any?,如果不想这样的话只要将上界改成Any就可以了。
mapOf()函数里的to解析——infix函数
之前,我们已经使用过很多次A to B这样的语法结构了。但to并不是一个关键字,能够实现这样的效果是因为Kotlin提供了一种语法糖特性:infix函数,这个函数也不难理解,比如A to B其实就是A.to(B)的写法。
要学习这个函数,我们就从例子开始吧。String类中的startsWith()相信你一定用过,其可以用来判断一个字符串是否是由某个指定参数开头的。虽然这个函数用起来很简单,但我们借助infix函数可以让代码可读性更强:
infix fun String.beginsWith(prefix:String) = startsWith(prefix)
这个函数很好理解,就是定义了一个String类的拓展函数,其实现就是直接调用startsWith()函数。但加上了infix关键字后,我们就可以使用另一种方式调用这个函数了:
if("Hello Kotlin" beginsWith "Hello")
这样看来,infix函数其实也没什么大不了的,只是让代码更加易读了而已。
那么我们直接打开to看看里面是如何实现的吧:
可以看到,这里使用定义泛型的方式将to()函数定义到了A类型下,并接收一个B类型的参数。实现就更为简单了,直接构建了一个Pair类型对象,传入了A和B对象。也就是说,A to B 其实就是得到了一个包含了A和B数据的Pair对象。
总结
这次,我们学习了高阶函数与安卓结合的用法,对泛型做了一个简单的解释。同时也对infix关键字做了简单的介绍。总的来说本次学习是一个过渡,主要用来巩固之前的知识。接下来估计会学习泛型的进阶特性。