一. 单例
使用 Java 来编写单例模式的话, 可以写出好几种. 同样, 使用 Kotlin 也可以写出多种单例模式. 在这里介绍的是一种使用委托属性的方式来实现单例的写法.
首先, Kotlin 在语法层面上支持委托模式.
委托模式是软件设计模式中的一项基本技巧. 在委托模式中, 有两个对象参与处理同一个请求, 接受请求的对象将请求委托给另一个对象来处理. 委托模式是一项基本技巧, 许多其他的模式, 如状态模式, 策略模式, 访问者模式本质上是在更特殊的场合采用了委托模式. 委托模式使得我们可以用聚合来替代继承.
对于一些很常见的属性, 虽然我们可以在每次需要它们的时候手动地实现它们, 但更好的方法是一次性全部实现, 然后放进一个库里面. 换句话说, 对其属性值的操作不再依赖于其自身的 getter()/setter()方法, 而是将其托付给一个代理类, 从而每个使用类中的该属性可以通过代理类统一管理. 这种方式是委托属性.
在 Kotlin 的标准库中有一系列的标准委托, not null 属性是其中之一. 它会含有一个可 null 的变量并会在我们设置这个属性的时候分配一个真实的值. 如果这个值在被获取之前没有被分配, 它就会抛出一个异常.
当然 by lazy 也可以实现单例, 下面我们使用 not null 委托来实现 Application 的单例.
- class App : Application() {
- companion object {
- var instance: App by Delegates.notNull()
- }
- override fun onCreate() {
- super.onCreate()
- instance = this
- }
- }
二. 封装 Extras
使用 ExtrasDelegate 来封装 Extras
- import android.support.v4.app.Fragment
- import android.support.v7.app.AppCompatActivity
- import kotlin.reflect.KProperty
- /**
- *
- * @FileName:
- * com.safframework.delegate.extras.Extras.kt
- * @author: Tony Shen
- * @date: 2018-06-11 23:56
- * @version V1.0 <描述当前版本功能>
- */
- class ExtrasDelegate<out T>(private val extraName: String, private val defaultValue: T) {
- private var extra: T? = null
- operator fun getValue(thisRef: AppCompatActivity, property: KProperty<*>): T {
- extra = getExtra(extra, extraName, thisRef)
- return extra ?: defaultValue
- }
- operator fun getValue(thisRef: Fragment, property: KProperty<*>): T {
- extra = getExtra(extra, extraName, thisRef)
- return extra ?: defaultValue
- }
- }
- fun <T> extraDelegate(extra: String, default: T) = ExtrasDelegate(extra, default)
- fun extraDelegate(extra: String) = extraDelegate(extra, null)
- @Suppress("UNCHECKED_CAST")
- private fun <T> getExtra(oldExtra: T?, extraName: String, thisRef: AppCompatActivity): T? =
- oldExtra ?: thisRef.intent?.extras?.get(extraName) as T?
- @Suppress("UNCHECKED_CAST")
- private fun <T> getExtra(oldExtra: T?, extraName: String, thisRef: Fragment): T? =
- oldExtra ?: thisRef.arguments?.get(extraName) as T?
封装完之后, 在 MainActivity 中传递参数跳转到其他到 Activity.
- import android.content.Intent
- import android.os.Bundle
- import android.support.v7.app.AppCompatActivity
- import com.safframework.delegate.R
- import com.safframework.delegate.domain.User
- import com.safframework.ext.click
- import kotlinx.android.synthetic.main.activity_main.*
- /**
- *
- * @FileName:
- * com.safframework.delegate.activity.MainActivity.java
- * @author: Tony Shen
- * @date: 2018-06-13 12:03
- * @version V1.0 <描述当前版本功能>
- */
- class MainActivity:AppCompatActivity() {
- public override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_main)
- initViews()
- }
- private fun initViews() {
- text1.click{
- val intent = Intent(this@MainActivity, Demo4ExtrasDelegateActivity::class.java)
- val u = User("Tony","123456")
- intent.putExtra("user",u)
- intent.putExtra("string","just a test")
- startActivity(intent)
- }
- text2.click {
- val intent = Intent(this@MainActivity, Demo4PrefsDelegateActivity::class.java)
- startActivity(intent)
- }
- }
- }
这里的 click 函数, 在使用 Kotlin 高效地开发 Android App(二)中已经讲述过, 就不在重复讲述.
Demo4ExtrasDelegateActivity 接受从 MainActivity 中传递过来的参数.
- import android.os.Bundle
- import android.support.v7.app.AppCompatActivity
- import com.safframework.delegate.domain.User
- import com.safframework.delegate.extras.extraDelegate
- import com.safframework.log.L
- /**
- *
- * @FileName:
- * com.safframework.delegate.activity.Demo4ExtrasDelegateActivity.java
- * @author: Tony Shen
- * @date: 2018-06-13 17:42
- * @version V1.0 <描述当前版本功能>
- */
- class Demo4ExtrasDelegateActivity: AppCompatActivity() {
- private val user: User? by extraDelegate("user")
- private val s:String? by extraDelegate("string")
- public override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- L.json(user)
- L.i(s)
- }
- }
所传递过来的任何对象类型, 都可以使用如下的方式获取 Extras. 只要保证, extra 的 key 正确即可.
- private val user: User? by extraDelegate("user")
- private val s:String? by extraDelegate("string")
与 Extra 类似, SharedPreferences 也可以使用属性委托的方式进行封装.
三. infix
中缀表达式是一种通用的算术或逻辑公式表示方法, 操作符以中缀形式处于操作数的中间. 中缀表达式允许我们使用一个单词或字母来当运算符用(其本质还是函数调用), 忽略调用的点和圆括号.
Kotlin 的中缀表达式, 需要满足以下条件:
使用 infix 修饰
只有一个参数
其参数不得接受可变数量的参数且不能有默认值.
例如:
- infix fun Int.add(i:Int):Int = this + i
- infix fun Int. 加(i:Int):Int = this + i
- fun main(args: Array<String>) {
- println(5 add 10)
- println(5 加 10)
- }
执行结果:
15 15
在 Kotlin 中, 使用中缀表达式最经典的例子, 莫过于使用 kxdate 来操作日期. kxdate github 地址: https://github.com/yole/kxdate
- val twoMonthsLater = 2.months.fromNow
- val yesterday = 1.days.ago
等价于:
- val twoMonthsLater = 2 months fromNow
- val yesterday = 1 days ago
由此可见, 中缀表达式能让代码看起来更加接近于自然语言.
四. inline
Kotlin 天生支持函数式编程, 高阶函数和 lambda 是其一大特色.
使用高阶函数会带来一些运行时间效率的损失: 每一个函数都是一个对象, 并且都会捕获一个闭包. 即那些在函数体内会被访问的变量. 内存分配 (对于函数对象和类) 和虚拟调用会引入运行时间开销.
使用 inline 修饰的函数, 可以从编译器角度 *** 将函数的函数体复制到调用处实现内联.***
在很多情况下, 通过将 Lambda 表达式内联在使用处, 可以消除运行时消耗.
翻看 Kotlin 的 Standard.kt 可以发现它里面的函数 with,apply,run,let 等都使用了 inline.
再举一个例子, 对 Closeable 进行扩展, 让它支持 Java 的 try-with-resources 特性.
- inline fun <T : Closeable?, R> T.use(block: (T) -> R): R {
- var closed = false
- try {
- return block(this)
- } catch (e: Exception) {
- closed = true
- try {
- this?.close()
- } catch (closeException: Exception) {
- }
- throw e
- } finally {
- if (!closed) {
- this?.close()
- }
- }
- }
该方法已经在 https://github.com/fengzhizi715/SAF-Kotlin-Utils 中
总结
本文是该系列最后一篇文章, 未来不会整理零碎的开发细节, 转而会以体系化形式进行整理.
来源: https://juejin.im/post/5b30de416fb9a00e9c47de55