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
        }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章