kotin之lateinit、by lazy、類型轉換、接口回調

kotlin中屬性在聲明的同時也要求初始化,否則會報錯,如下圖示例

private var name0: String //報錯
private var name1: String = "xiaoming" //不報錯
private var name2: String? = null //不報錯

可是這樣問題來了,有時候並不想聲明一個可空的對象,而且也沒法在聲明的時候就爲它初始化,那麼這時需要用到kotlin中提供的延遲初始化。

Kotlin中提供了延遲初始化的方式,一種是lateinit var ,一種是 by lazy

一、lateinit 延遲初始化

private lateinit var name:String
  • lateinit var 只能修飾類屬性,不能用來修飾局部變量,並且只能用來修飾對象,不能用來修飾基本類型(因爲基本類型的屬性在類加載後的準備階段都會被初始化爲默認值)

  • lateinit var 的作用就是讓編譯期間在檢查時不要因爲屬性變量未被初始化而報錯

  • Lateinit var 在Android中的基本使用

  • abstract class BaseFragment : Fragment() {
    
        protected lateinit var mContext: Context
        override fun onAttach(context: Context) {
            super.onAttach(context)
            mContext = context
        }
    }
    

二、by lazy延時初始化

  • by lazy 本身是一種屬性委託,屬性委託的關鍵字是by。
    by lazy 的寫法如下
//用於屬性延遲初始化
val name: Int by lazy { 1 }
 
//用於局部變量延遲初始化
public fun foo() {
    val bar by lazy { "hello" }
    println(bar)
}
  • by lazy要求屬性聲明爲val,即不可變變量,在java中相當於被final修飾。這意味着該變量一旦初始化後就不允許再被修改值了(基本類型是值不能被修改,對象類型是引用不能被修改)。{}內的操作就是返回唯一一次初始化的結果。

  • by lazy可以使用於類屬性或者局部變量。

    class MainActivity : AppCompatActivity() {
    
       private val bt by lazy {
            findViewById<Button>(R.id.bt)
        }
        
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            bt.setOnClickListener {
                Toast.makeText(baseContext, "click", Toast.LENGTH_SHORT).show()
            }
        }
    }
    

三、kotlin中by關鍵字的作用:

  • 如by lazy 關鍵字在Android中的使用:
class MainActivity : AppCompatActivity() {
    private val viewModel by  viewModels<CheeseViewModel>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

@MainThread
inline fun <reified VM : ViewModel> ComponentActivity.viewModels(
    noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
    val factoryPromise = factoryProducer ?: {
        defaultViewModelProviderFactory
    }

    return ViewModelLazy(VM::class, { viewModelStore }, factoryPromise)
}
  • viewModel 則採用by lazy這種方式進行延時初始化
//kotlin 封裝:
fun <V : View> Activity.bindView(id: Int): Lazy<V> = lazy {
    viewFinder(id) as V
}
 
//acitivity中擴展調用
private val Activity.viewFinder: Activity.(Int) -> View?
    get() = { findViewById(it) }
 
 
//在activity中的使用姿勢
val mTextView by bindView<TextView>(R.id.text_view)
mTextView.text="執行到我時,纔會進行控件初始化"
1.將接口的實現委託給另一個對象

委託模式已經證明是實現繼承的一個很好的替代方式, 而 Kotlin 可以零樣板代碼地原生支持它。 Derived 類可以通過將其所有公有成員都委託給指定對象來實現一個接口 Base

interface Base {
    fun print()
}

class BaseImpl(val x: Int) : Base {
    override fun print() { print(x) }
}

class Derived(b: Base) : Base by b

fun main() {
    val b = BaseImpl(10)
    Derived(b).print()
}

Derived 的超類型列表中的 by-子句表示 b 將會在 Derived 中內部存儲, 並且編譯器將生成轉發給 b 的所有 Base 的方法。

2.將屬性訪問器的實現委託給另一個對象

四、as關鍵字和is關鍵字的使用

1、Kotlin 類型轉換的操作 (聲明式)

if(object instanceof Car){
}
Car car = (Car) object
if(object is Car){
}
var car= object as Car

2、Kotlin 類型轉換操作(隱式)

if(object instanceof Car){
	Car car = (Car)object;
}
if(object is Car){
	var car= object //聰明的轉換
}

五、回調函數的Kotin的lambda的簡化

在Kotlin中對Java中的一些的接口的回調做了一些優化,可以使用一個lambda函數來代替。可以簡化寫一些不必要的嵌套回調方法。但是需要注意:在lambda表達式,只支持單抽象方法模型,也就是說設計的接口裏面只有一個抽象的方法,才符合lambda表達式的規則,多個回調方法不支持。

1、用Java代碼實現一個接口的回調。


 mView.setEventListener(new ExamPlanHomeEventListener(){
 
    public void onSuccess(Data data){
      //todo
    }
 
 });

2、在Kotlin中的實現一個接口的回調,不使用lambda表達式(這種方式非常適用於kotlin中對於一個接口中含有多個回調方法)。

mView.setEventListener(object: ExamPlanHomeEventListener{
     
    public void onSuccess(Data data){
      //todo
    }
     
});

3、如果在Kotlin中的對於接口只有一個回調的方法,就符合使用lambda函數,我們可以把以上代碼簡化成這樣。

mView.setEventListener({
   data: Data ->
   //todo
})

//或者可以直接省略Data,藉助kotlin的智能類型推導

mView.setEventListener({
   data ->
   //todo
})

4、如果以上代碼中的data參數沒有使用到的話,可以直接把data去掉

mView.setEventListener({
  //todo

})

5、以上代碼還可以做個調整,由於setEventListener函數最後一個參數是一個函數的話,可以直接把括號的實現提到圓括號外面

mView.setEventListener(){
   //todo
}

6、由於setEventListener這個函數只有一個參數,可以直接省略圓括號

mView.setEventListener{
  //todo
}

7、Android中常見的接口回調使用

   vp_fp_viewpager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
            override fun onPageSelected(position: Int) {
                val tabCount: Int = tl_fp_tablayout.getTabCount()
                for (i in 0 until tabCount) {
                    val tab: TabLayout.Tab? = tl_fp_tablayout.getTabAt(i)
                    val customView = tab?.customView as TextView?
                    if (tab?.position == position) {
                        customView!!.textSize = 16f
                        customView.typeface = Typeface.DEFAULT_BOLD
                    } else {
                        customView!!.textSize = 12f
                        customView.typeface = Typeface.DEFAULT
                    }
                }
            }
        })
        flowLayout.setOnTagClickListener { view, position, parent ->
            if (item != null) {
                var bundle = Bundle()

                bundle.putString(TITLE, item.children[position].name)
                bundle.putInt(CID, item.children[position].id)

                var intent = Intent(mContext, SystemDetailActivity::class.java)
                intent.putExtras(bundle)
                mContext.startActivity(intent)
            }
            true
        }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章