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

kotlin 踩坑記 三

這次碰到的是這個異常,記錄如下:

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.xianglin.app/com.xianglin.app.biz.chat.groupsetting.scangroup.ScanGroupActivity}: kotlin.UninitializedPropertyAccessException: lateinit property view has not been initialized
    ║     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2655)

 原因很簡單,採用lateinit標記屬性時未初始化。lateinit是防止出現空的。程式碼如下:

class ScanGroupPresenter(scanGroupFragment: ScanGroupFragment) : ScanGroupContract.Present {
//    private var view: ScanGroupContract.View ?= null  //允許為空
    private lateinit var view :ScanGroupContract.View   //非空如何初始化
    init {
//        if (view == null) return
        this.view = view
        view.setPresenter(this)
    }
在這個類的建構函式進行賦值,在其它地方呼叫  ScanGroupPresenter(scanGroupFragment) 所以就出錯了。

修改後:

class ScanGroupPresenter(scanGroupFragment: ScanGroupFragment) : ScanGroupContract.Present {

//    private var view: ScanGroupContract.View ?= null  //允許為空
    private var view :ScanGroupContract.View = scanGroupFragment  //非空如何初始化

    init {
//        if (view == null) return

//        this.view = view
        view.setPresenter(this)
    }

//    constructor(scanGroupFragment: ScanGroupFragment):this(scanGroupFragment)



//    fun ScanGroupPresenter(view: ScanGroupContract.View?){
//        if (view == null) return
//        this.view = view
//        view.setPresenter(this)
//    }

    override fun getGroupInfo(groupId: String) {
        if (TextUtils.isEmpty(groupId)) return
        val list = ArrayList<Any>()
        list.add(groupId)
        RetrofitUtil.createService()
                .queryGroupByRUId(RpcHelper.getParamMap(ApiMethod.METHOD_QUERY_GROUP_BYRUID, list))
//                .compose<Response<GroupVo>>(TransformUtils.defaultSchedulers<Response<GroupVo>>(view))
                .compose(TransformUtils.defaultSchedulers(view))
                .subscribe(object : HttpResponseSubscriber<GroupVo>() {

                    override fun onSubscribe(@NonNull d: Disposable) {
                        super.onSubscribe(d)
                    }

                    override fun onSuccess(result: GroupVo?) {
                        if (result == null) return
                        view.showSuccess(result)

                    }

                    override fun _onError(e: HttpThrowable) {
                        view.showTips(e.message!!)
                    }
                })


    }

對於kotlin的建構函式一般有如下幾種方式:

Kotlin的建構函式分為主構造器(primary constructor)和次級構造器(secondary constructor)。下面我們來看看他們的寫法。

一、 Primary Constructor

1. 寫法一:

class 類名 constructor(形參1, 形參2, 形參3){}
eg: 

  1. class Person constructor(username: String, age: Int){

  2. private val username: String

  3. private var age: Int

  4.  
  5. init{

  6. this.username = username

  7. this.age = age

  8. }

  9. }

這裡需要注意幾點:

  • 關鍵字constructor:在Java中,構造方法名須和類名相同;而在Kotlin中,是通過constructor關鍵字來標明的,且對於Primary Constructor而言,它的位置是在類的首部(class header)而不是在類體中(class body)。
  • 關鍵字init:init{}它被稱作是初始化程式碼塊(Initializer Block),它的作用是為了Primary Constructor服務的,由於Primary Constructor是放置在類的首部,是不能包含任何初始化執行語句的,這是語法規定的,那麼這個時候就有了init的用武之地,我們可以把初始化執行語句放置在此處,為屬性進行賦值。

2. 寫法二(演變一):

a. 當constructor關鍵字沒有註解和可見性修飾符作用於它時,constructor關鍵字可以省略(當然,如果有這些修飾時,是不能夠省略的,並且constructor關鍵字位於修飾符後面)。那麼上面的程式碼就變成:

  1. class Person (username: String, age: Int){

  2.     private val username: String

  3.     private var age: Int

  4.  
  5.     init{

  6. this.username = username

  7. this.age = age

  8.     }

  9. }

b. 初始化執行語句不是必須放置在init塊中,我們可以在定義屬性時直接將主構造器中的形參賦值給它。

  1. class Person(username: String, age: Int){

  2.     private val username: String = username

  3.     private var age: Int = age

  4. }

可以看出,我們的寫法二實際上就是對我們在寫法一前面提到的兩個關鍵字的簡化。

3. 寫法三(演變二):
這種在構造器中宣告形參,然後在屬性定義進行賦值,這個過程實際上很繁瑣,有沒有更加簡便的方法呢?當然有,我們可以直接在Primary Constructor中定義類的屬性。

class Person(private val username: String, private var age: Int){}

如果類不包含其他操作函式,那麼連花括號也可以省略

class Person(private val username: String, private var age: Int)

看,是不是一次比一次簡潔?實際上這就是Kotlin的一大特點

4.    當我們定義一個類時,我們如果沒有為其顯式提供Primary Constructor,Kotlin編譯器會預設為其生成一個無參主構造,這點和Java是一樣的。比如有這樣的一個類:

  1. class Person {

  2. private val username = "David"

  3. private var age = 23

  4.  
  5. fun printInfo() {

  6. println("username = $username, age = $age")

  7. }

  8. }

  9. fun main(args: Array<String>) {

  10. val person = Person()

  11. person.printInfo()

  12. }

  13.  

我們使用javap命令來反編譯這個Person類。可以看到這個無參主構造:

二、 Secondary Constructor
1.示例:

  1. class User{

  2. private val username: String

  3. private var age: Int

  4.  
  5. constructor(username: String, age: Int){

  6.     this.username = username

  7.     this.age = age

  8. }

  9. }

和Primary Constructor相比,很明顯的一點,Secondary Constructor是定義在類體中。第二,Secondary Constructor可以有多個,而Primary Constructor只會有一個。

2. 要想實現屬性的初始化,實際上主構造器已經能夠應付多數情況了,為什麼還需要次級構造器?主要原因是因為我們有時候是需要去繼承框架中的類。如在Android中你自定義一個Button:

 
  1. class MyButton : AppCompatButton {

  2.  
  3. constructor(context: Context) : this(context, null)

  4.     constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, R.attr.buttonStyle)

  5.     constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)

  6. }

在這種情況下,你如果需要重寫AppCompatButton的多個構造器,那麼,就需要用到Secondary Constructor。同樣,這裡也有幾點需要注意:

  • 可以看到,我們可以使用this關鍵字來呼叫自己的其他構造器,並且需要注意它的語法形式,次級構造器: this(引數列表)
  • 可以使用super關鍵字來呼叫父類構造器,當然這塊內容我們放到繼承那塊再來介紹。

3. 我們再來看這樣一種情況,我們同時定義了主構造器和次級構造器:

  1. class Student constructor(username: String, age: Int) {

  2.  
  3.  
  4. private val username: String = username

  5. private var age: Int = age

  6. private var address: String

  7. private var isMarried: Boolean

  8.  
  9.  
  10. init {

  11. this.address = "Beijing"

  12. this.isMarried = false

  13. }

  14.  
  15.  
  16. constructor(username: String, age: Int, address: String) :this(username, age) {

  17.  
  18. this.address = address

  19. }

  20.  
  21.  
  22. constructor(username: String, age: Int, address: String, isMarried: Boolean) : this(username, age, address) {

  23.  
  24. this.isMarried = isMarried

  25. }

  26.  
  27.  
  28. }

可以看到,四個引數的次級構造呼叫三個引數的次級構造,而三個引數的次級構造又呼叫了主構造。換句話,次級構造會直接或者間接呼叫主構造。這也就是這個例子需要說明的問題。如果你還不信,我們可以把“: this(username, age)”刪除掉,看編譯器會給你什麼提示:

“Primary constructor call expected”。提示很明顯,和我們在之前提到的內容是一樣的。

參考:https://blog.csdn.net/qq_26585943/article/details/80275426