一. 單例
使用 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 中
總結
本文是該系列最後一篇文章,未來不會整理零碎的開發細節,轉而會以體系化形式進行整理。
該系列的相關文章:
Java與Android技術棧:每週更新推送原創技術文章,歡迎掃描下方的公眾號二維碼並關注,期待與您的共同成長和進步。