kotlin專案開發總結
前言
最近都是在用Kotlin開發Android專案,總結了一些心得在這裡和大家分享
1. 定義變數
kotlin定義變數有三種形式
1)使用var定義可修改變數,最常見的用法,也是很靈活,
private var point: Point? = null
//使用的時候,因為point是可空的,所以有兩種用法
println(point?.x) //如果你不能確保point是否為空
println(point!!.x) //如果你能確保point一定不為空,否則point為空,這裡會報執行時空指標
//下面用法會報編譯時錯誤,因為point是Point?型別,所以point有可能是null
println(point.x)
從上面的註釋,我們可以發現,kotlin從編譯時預防了空指標的可能性,VeryGood的特性,能夠避免很多人為導致的空指標錯誤
2)使用val定義不可修改的變數
private val point = Point(20, 20)
這種情況下point一定不會為空,因為val定義的變數必須要初始化,從這個角度來看,又避免了人為導致的空指標錯誤
比如下面會產生編譯時錯誤
point = Point(30, 30)
需要注意的是,point不能被修改,但是Point類裡面的成員變數是可修改的,所以下面操作是允許的
point.x = 30
point.y = 30
3)使用lateinit var,可以使得變數的初始化可以延遲到需要的時候
比如在使用dagger的時候,需要inject,如下
@Inject lateinit var mPresenter: MapHomePresent
但是需要慎用lateinit, 因為你可能後面忘記了初始化,但是編譯器又不會報錯提醒。只有執行之後才會檢測到,如下
fun main(args: Array<String>) {
var test = Test()
println(test.a)
}
class Test {
lateinit var a: String
}
會報錯如下:
Exception in thread "main" kotlin.UninitializedPropertyAccessException: lateinit property a has not been initialized
at Test.getA(Simplest version.kt:14)
at Simplest_versionKt.main(Simplest version.kt:10)
2.定義方法
注意下面是兩種型別
var a: String? = null
var b: String = null
b可以自動轉換成a, a需要使用a!!轉換成b,這種在定義方法的時候特別有用,如下舉例
fun test(a: String) {
a.apply{
print(this)
}
}
上面這個方法在傳入引數時,必須保證這個引數不為空,比如下面會報編譯錯誤
var temp: String? = null
test(temp)//這裡會報編譯錯誤,L型別自動轉換
test(temp!!)//ok,但是需要在別的地方把temp重新賦值為非null
反過來,下面是可以呼叫的
fun test(a: String?) {
a?.apply{
print(this)
}
}
var temp: String = null
test(temp)//會自動轉換
3. 通過data class 定義entity
可以看下data class的定義,它是專門用來儲存資料的,很適合entity的場景,比如從伺服器拉取資料。
4. 多使用Kotlin提供的標準函式
比如使用apply函式,可以使得程式碼看起來非常簡潔
mOption.apply {
locationMode = AMapLocationClientOption.AMapLocationMode.Hight_Accuracy//可選,設定定位模式,可選的模式有高精度、僅裝置、僅網路。預設為高精度模式
isNeedAddress = false
/**
* 設定是否優先返回GPS定位結果,如果30秒內GPS沒有返回定位結果則進行網路定位
* 注意:只有在高精度模式下的單次定位有效,其他方式無效
*/
isGpsFirst = false // GPS優先會導致反應速度很慢
// 設定是否開啟快取
isLocationCacheEnable = true
// 設定是否單次定位
isOnceLocation = true
//設定是否等待裝置wifi重新整理,如果設定為true,會自動變為單次定位,持續定位時不要使用
isOnceLocationLatest = true
//設定是否使用感測器
isSensorEnable = true
interval = 1000
// 設定網路請求超時時間
httpTimeOut = 60000
//設定是否開啟wifi掃描,如果設定為false時同時會停止主動重新整理,停止以後完全依賴於系統重新整理,定位位置可能存在誤差
isWifiScan = true
}
對比下,下面的普通用法
mOption.locationMode = AMapLocationClientOption.AMapLocationMode.Hight_Accuracy//可選,設定定位模式,可選的模式有高精度、僅裝置、僅網路。預設為高精度模式
mOption.isGpsFirst = false//可選,設定是否gps優先,只在高精度模式下有效。預設關閉
mOption.httpTimeOut = 30000//可選,設定網路請求超時時間。預設為30秒。在僅裝置模式下無效
mOption.interval = 2000//可選,設定定位間隔。預設為2秒
mOption.isNeedAddress = true//可選,設定是否返回逆地理地址資訊。預設是true
mOption.isOnceLocation = false//可選,設定是否單次定位。預設是false
mOption.isOnceLocationLatest = false//可選,設定是否等待wifi重新整理,預設為false.如果設定為true,會自動變為單次定位,持續定位時不要使用
AMapLocationClientOption.setLocationProtocol(AMapLocationClientOption.AMapLocationProtocol.HTTP)//可選, 設定網路請求的協議。可選HTTP或者HTTPS。預設為HTTP
mOption.isSensorEnable = false//可選,設定是否使用感測器。預設是false
mOption.isWifiScan = true //可選,設定是否開啟wifi掃描。預設為true,如果設定為false會同時停止主動重新整理,停止以後完全依賴於系統重新整理,定位位置可能存在誤差
mOption.isLocationCacheEnable = true //可選,設定是否使用快取定位,預設為true
一對比發現,上面這種寫法真的是很簡潔
5. 巧用“?:”
?: 的意思是,左邊的表示式沒有成功,則使用右邊的結果;如下,person是null,所以person?.name
不會執行,所以最終a == "null"
var person: Person? = null
var a = person?.name ?: "null"
配合閉包也可以使用,如下程式碼:
screenMarker?.apply {
val point = aMap!!.projection.toScreenLocation(position)
point.y -= SizeUtils.sp2px(125f)
val target = aMap!!.projection.fromScreenLocation(point)
val animation = TranslateAnimation(target)
animation.setInterpolator { input ->
// 模擬重加速度的interpolator
if (input <= 0.5) {
(0.5f - 2.0 * (0.5 - input) * (0.5 - input)).toFloat()
} else {
(0.5f - Math.sqrt(((input - 0.5f) * (1.5f - input)).toDouble())).toFloat()
}
}
//整個移動所需要的時間
animation.setDuration(600)
//設定動畫
setAnimation(animation)
//開始動畫
startAnimation()
}?:KLog.d(TagObject.TAG, "screenMarker is null")
是不是看上去,程式碼連貫性很強。
6. 使用companion object
當你的類包含太多的東西,你想把它們隔離到另外一個類,又不想使用類引用的方式,你就可以使用companion object,如下
class AndroidFragment : MainFragment() {
override fun getAdapter(list: ArrayList<FuckGoods>): BaseBindingAdapter<*> {
return FuckGoodsAdapter(list)
}
override fun getType(): String {
return ANDROID
}
//companion object的好處是,外部類可以直接訪問物件,不需要通過物件指標
companion object {
val ANDROID = "ANDROID"
fun newInstance(): Fragment {
val fragment = AndroidFragment()
val bundle = Bundle()
fragment.arguments = bundle
return fragment
}
}
}
可以結合一起使用,又能實現程式碼分離,增強程式碼的可讀性
7. 通過閉包減少介面類
有些時候你不想使用定義新的介面去實現回撥,那就可以考慮使用閉包。如下程式碼:
protected fun <T> submit(observable: Observable<JsonResult<T>>, block: (T) -> Unit) {
addDisposable(
observable.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if(it != null && !it.error && it.results != null) {
block(it.results)
}else if (it == null){
KLog.d(TagObject.TAG_Release, "result is null")
}else {
KLog.d(TagObject.TAG_Release, "res.error:" + it.error + ",res.results:" + it.results)
}
},
{
KLog.d(TagObject.TAG_Release, "error android Presenter" + it.message)
}
)
)
}
呼叫如下:
submit(mModel.getData(page, type)){
view.setData(it)
}
這樣寫程式碼,會顯示非常簡潔,也避免了建立大量的介面回撥類。
8. Java類轉換成kotlin
首先選中Java類,然後按ctrl+shift+A,彈出一個“enter action or option name”的對話方塊,然後輸入”convert Java file to kotlin file”,就像下面的截圖:
點選“Convert java File to Kotlin File”(也可以使用快捷鍵),IDE就自動幫我們把Java檔案轉換成kotlin, 一般情況下,轉換出來的檔案,但是不排除需要做一些修改。但是相對來說也省事多了