1. 程式人生 > >kotlin 踩坑記一

kotlin 踩坑記一

kotlin 都出來一年多了,好多公司依然用起來了,新技術必須學習,廢話不多說,開始踩坑。碰到bug問題,現在做一一記錄。

碰到的第一個問題:Smart cast to 'Node' is impossible, because 'left' is a mutable property that could have been changed by this time

錯誤程式碼如下:

private var scanGroupFragment : ScanGroupFragment ?= null
override fun getFragment(): BaseFragment {
    scanGroupFragment = ScanGroupFragment()
    if (mBundle != null) {
        scanGroupFragment.arguments = mBundle
    }
    return scanGroupFragment

}

原因是可能導致scanGroupFragment為空值。

解決方法一:加!!(不推薦)

override fun getFragment(): BaseFragment {
    scanGroupFragment = ScanGroupFragment()
    if (mBundle != null) {
        scanGroupFragment!!.arguments = mBundle
    }
    return scanGroupFragment!!
}

如下stack上的解決方法: 

  1. Use a local variable with smart cast:

    val node = left
    if (node != null) {
        queue.add(node)
    }
  2. Use a safe call such as one of the following:

    left?.let { node -> queue.add(node) }
    left?.let { queue.add(it) }
    left?.let(queue::add)
  3. Use the Elvis operator with return to return early from the enclosing function:

    queue.add(left ?: return)

    Note that break and continue can be used similarly for checks within loops.

這樣就可以編譯通過了。

參考:https://stackoverflow.com/questions/44595529/smart-cast-to-type-is-impossible-because-variable-is-a-mutable-property-tha/44596284

https://blog.csdn.net/huangpin815/article/details/79283280

然而,這種解決方式不推薦,會出現異常。看到!!符號。這意味著“你在這裡有一個潛在的未處理的KotlinNullPointerException”。

先寫解決方案:

private lateinit var scanGroupFragment : ScanGroupFragment
override fun getFragment(): BaseFragment {
    scanGroupFragment = ScanGroupFragment()
    if (mBundle != null) {
        scanGroupFragment.arguments = mBundle
    }
    return scanGroupFragment
}

下面介紹幾種方式可以去避免使用!!

① 使用val而不是var

Kotlin讓你在語言層面思考不可變性。val是隻讀的,var是可變的。建議你儘可能多的使用只讀屬性。它們是執行緒安全的,並且在函數語言程式設計中很好用。

② 使用lateinit

有時候,我們不能使用不可變屬性。這在Android中很常見。對於這種情況,我們使用Kotlin提供的lateinit

!!的寫法

private var mAdapter: RecyclerAdapter? = null

override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   mAdapter = RecyclerAdapter(R.layout.item_data)
}

fun updateData() {
   mAdapter!!.notifyDataSetChanged()
}

lateinit寫法

private lateinit var mAdapter: RecyclerAdapter<>

override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   mAdapter = RecyclerAdapter(R.layout.item_data)
}

fun updateData() {
   mAdapter.notifyDataSetChanged()
}

注意:訪問非初始化lateinit屬性將導致UninitializedPropertyAccessException。

lateinit不能應用於原始資料型別(例如:Int)。對於原始資料型別,我們可以這樣使用委託:

private var mNum:Int by Delegates.notNull <Int>()

③ 使用let函式

下面是Kotlin程式碼中常見的錯誤


這個可變屬性在空檢查後不能被改變。許多人用下面這種方式解決:

 

private var mPhotoUrl: String? = null

fun uploadClicked() {
    if (mPhotoUrl != null) {
        uploadPhoto(mPhotoUrl!!)
    }
}

但是可以用let函式更優雅的解決這個問題:

private var mPhotoUrl:String?=null
fun uploadClicked(){
    mPhotoUrl?.let{uploadPhoto(it)}
}

④ 建立全域性函式來處理更復雜的內容

let是一個簡單的空檢查的替代品,但是會有更復雜的情況。如:

if(name!=null&&address!=null{
    upload(name!!,address!!)
}

你可以巢狀兩個let,但是可讀性會很差。這時候我們用下面這種方式來寫:

ifNotNull(name,address){
    name,address->upload(name,address)
}

封裝的方法

fun <T1,T2> ifNotNull(value1:T1?,value2:T2?,bothNotNull:(T1,T2)->(Unit)){
    if(value1!=null&&value2!=null){
        bothNotNull(value1,value2)
    }
}

⑤ 使用?:操作符

fun getName():String{
    if(name!=null){
        return name!!
    }else{
        return "android coder"
    }
}

替代的方法:

fun getName():String{
    return name?:"android coder"
}

⑥ 自定義錯誤資訊

使用內建函式requireNotNull或checkNotNull處理異常資訊。

upload(intent.getStringExtra("address")!!)

替代方法:

upload(requireNotNull(intent.getStringExtra("address"),{"'address'引數為空!"})

參考:https://blog.csdn.net/xiaoluoli88/article/details/78082311

ok以上便解決完成了。