Kotlin进阶学习3

写在前面

本文上接: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更是很早就支持了泛型。篇幅原因,这里就先只学习一下泛型的基本用法。那什么是泛型呢?泛型其实就是允许我们在不指定具体类型的情况下进行编程,极大地方便了我们的开发和拓展。

泛型主要有两种定义方式,泛型类和泛型方法。使用的语法结构都是,当然,T并不是固定的,只是我们约定俗成的一种写法。

泛型类

要定义一个泛型类,如下:

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关键字做了简单的介绍。总的来说本次学习是一个过渡,主要用来巩固之前的知识。接下来估计会学习泛型的进阶特性。

posted @ 2020-08-10 16:06  武神酱丶  阅读(389)  评论(1编辑  收藏  举报