美文网首页
kotlin学习记录

kotlin学习记录

作者: 沉迷学习_日渐发福 | 来源:发表于2018-11-18 11:29 被阅读0次

    kotlin

    将一个activity的java文件转为kotlin文件

    最简单的转化方式

    android stutdio支持一键转化的方式,通过快捷键ctrl +alt + shift + k将整个java文件转化为kt文件,不过可能需要自己解决一些代码不一致的冲突,而且有些时候转化后的代码和我们直接写的代码还是有差别的。

    环境配置

    kotlin库引入

    android studio3.0默认支持kotlin,虽然是默认支持kotlin,但也是需要增加一些配置,不过这个as已经给我们做好了智能提示和自动添加的功能。在我们第一次创建出kt文件的时候,将会有一个kotlin的配置提示,我们只需要点击configure,然后选择全局配置,那么as将会在gradle中添加上kotlin的依赖
    [图片上传失败...(image-4852fe-1542511738777)]
    gradle文件如下所示:
    [图片上传失败...(image-17de9f-1542511738777)]
    这样我们就使用kotlin的代码库了。

    kotlin extension库引入

    之前项目中用的是view的绑定库是ButterKnife,就个人而言,还是觉得挺好用的,毕竟绑定View的代码都能自动生成,不过如果在kt文件中,不能直接引入这个库,需要重新引入kotlin中的ButterKnife库https://github.com/JakeWharton/kotterknife,不过我们可以不用这个库了,因为kotlin给我们提供了一套更为简便的库,我们只需要在gradle中引入kotlin-extension,并且在kt文件中import相对应的xml,就能直接通过id的方式引用view。

    //在kt文件中
    import kotlinx.android.synthetic.main.activity_member_center.*
    
    //在gradle文件中
    apply plugin: 'kotlin-android-extensions'
    

    比如说我们在xml中有这样一个View:

     <android.support.v7.widget.RecyclerView
            android:id="@+id/memberRv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_below="@+id/member_center_tool_bar"
            android:layout_centerHorizontal="true" />
    

    这样我们在kt文件中,可以像下面的方式这样调用:

    private fun initRv() {
            adapter = MemberInfoAdapter(this, null)
            memberRv.layoutManager = LinearLayoutManager(this)
            memberRv.itemAnimator?.changeDuration = 0
            memberRv.adapter = adapter
        }
    

    这个memberRv就是RecyclerView在xml中指定的id。

    一些常用用法

    参数定义

    修饰符

    在kotlin中,声明所有变量都需要3个关键字修饰,var、val和const。在kotlin中是没有final修饰符的,final修饰符在kotlin中其实就相当于val

    • var:主要是用来修饰可变变量的。
    • val:主要是用来修饰不可变变量的,其实也不能说是不可变,更好的称呼是叫做只读变量,val在修饰变量的时候可以不初始化,但是它不能被赋值,在java中,用final修饰的变量必须要初始化,否则编译不能通过。举个val修饰不初始化的例子:
          class a {
          var c = 1;
          val b: String
          fun getB(): String {
              return c > 3 ? "haha" : "xixi"
          }
          
          fun add() {
              c++
          }
          
          }
      
    • const:其实const就和java中的static final一致了,不能放在局部方法中,在编译期间必须确定变量值。
    静态变量及静态方法

    在java中,静态属性和静态方法只需要一个static属性就能搞定了,但是在kotlin,它有一个单独的块来标识静态块的初始化,companion object:

    companion object {
            val intentFrom: String = "intent_extra"
            fun startMemberCenterActivity(context: Context,                 launchMemberCenter: LaunchMemberCenter) {
                var intent = Intent(context, TestAc::class.java)
                intent.putExtra(intentFrom, launchMemberCenter)
                if (context is Activity) {
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                }
                context.startActivity(intent)
            }
        }
    

    这个代码块有几个需要注意的点,

    • 在kt定义Intent中,class的指定需要已类名::class.java的方式指定,否则在log中会提示找不到activity
    • 在这个静态块中,定义的变量和方法都是静态的
    • 在外部如果需要访问这个块的方法,如果是kotlin文件访问,可以直接通过类名.的方式调用,如果是java文件访问,需要通过类名.companion.的方式调用:
    //在java文件中调用
    public void start() {
           TestAc.Companion.startMemberCenterActivity(context, this);
       }
    //在kt文件中调用
    public void start() {
           TestAc.startMemberCenterActivity(context, this);
       }
    
    kotlin的空指针安全

    在变量定义时,我们可以通过kotlin特有的方式来标识变量是否可空:

    private var adapter: MemberInfoAdapter? = null
    

    只需要的变量类型后面添加?,就表示这个变量是可以为null的,如果不添加,默认这个变量不能为null。一旦我们添加了?,在后续的代码中,如果有引用到这个变量的地方,如果没有做判空处理将会编译不过,这个判空处理还是挺方便的,

     override fun refreshDiscoveryList(mixFindInfoList: MutableList<MixFindInfo>?) {
            adapter?.notifyMixFindInfoChange(mixFindInfoList)
        }
    

    Equality check should be used instead of elvis for nullable boolean check
    在调用时,如果adaper为空了,将不会执行后续的操作。
    今天遇到了一个符号:

    var xie = a ?: ""
    

    这个?:是kotlin的elvis用法,属于两目运算符,就是简单的if、else缩写,
    当a不为null,取a的值,当a为null,取""

    接口使用

    kotlin接口和java接口的使用区别

    class TestAc : BaseMvpActivity<MemberCenterPresent>(), MemberCenterPresent.MemberInfoChange,
            KKAccountManager.KKAccountChangeListener, View.OnClickListener {
    

    其中BaseMvpActivity继承,而其他三个都是接口继承。

    • java接口需要用implements关键字实现,而kotlin实现的方式和继承一样
    • java接口能够直接通过匿名内部类创建出来,而kotlin也需要先将接口对象创建出来

    when表达式

    在kotlin中,用when表达式将将java中的switch替换掉了。
    最简单的形式如下:

     override fun onClick(v: View?) {
           when (v?.id) {
               R.id.icBack -> finish()
               R.id.btnOpenLayout -> btnOpenLayoutClick()
               else -> {
               
           }
       }
    

    而且,when里面的条件判断可以加入比较复杂的判断,如下:

    when (x) {
       in 1..10 -> print("x is in the range")
       in validNumbers -> print("x is valid")
       !in 10..20 -> print("x is outside the range")
       else -> print("none of the above")
    }
    

    继承

    kotlin中当含有非空的构造函数得继承,有以下这几种方式:

    class MemberCenterAdapter(context: Context?, mixFindInfoList: ArrayList<MixFindInfo>?) : TopicTabListAdapter(context, mixFindInfoList) 
    

    相当于子类的构造器声明就放在了class的定义上,然后会对应父类相应的构造器。还有另外一种实现方式,主要用于多个构造器的声明。

    constructor(context: Context?, mixFindInfoList: ArrayList<MixFindInfo>?): super(context, mixFindInfoList)
    

    列表

    列表元素访问

    在kotlin中,推荐我们使用下标的方式来访问元素,就像在访问数组一样。

    private var itemTypeList: ArrayList<Int> = ArrayList()
    itemTypeList[2] = ITEM_TYPE_ACTIVITY
    

    列表元素遍历

    对于kotlin中的列表遍历,增加了许多遍历方式。最简单的如下所示:

    //kotlin 
    for (i in startIndex..mixFindInfoList!!.size )
    //java
    for(int i = startIndex; i < mixFindInfoList.size(); i++)
    

    在kotlin中,推荐我们使用until的方式进行遍历,这样,我们处理起来也就像是在处理流一样。

    // a until b表示从a到b进行遍历,并且将遍历的值传递给下游,
    // map将上游传递过来的值进行转化,并传递给下游
    // forEach对转化后的值进行处理
    (startIndex until mixFindInfoList!!.size)
                    .map { mixFindInfoList[it] }
                    .forEach {
                        itemTypeList.add(it.item_type)
                    }
    

    三目运算符

    之前在java中用惯了三目运算符,看着kotlin的三目运算符挺不习惯的。

    //java, 如果a为true,则取b,否则取c 
    a ? b : c
    //kotlin, 利用if else组成三目运算符,但是不需要括号
    if(a) b else c
    

    函数中流程语句的返回值

    在kotlin中,如果多个分支语句都返回值,那么我们可以将返回值放在分支语句的外侧。

    //最原始的版本
    when (position) {
        0, 2 -> return null
        1 -> return Utility.getSafely(mixFindInfoList, 0)
        else -> {
            return Utility.getSafely<MixFindInfo>(mixFindInfoList, position - 2)
                }
                    }
    

    优化后的版本:

    return when (position) {
        0, 2 -> null
        1 -> Utility.getSafely(mixFindInfoList, 0)
        else -> {
        Utility.getSafely<MixFindInfo>(mixFindInfoList, position - 2)                }
                    }
    

    这种时候,就能够返回每一个when分支对应的值
    对于if else语句也能这么使用

     return if (activityListExist()) {
                if (vipBannerExist()) {
                    when (position) {
                        0, 2 -> null
                        1 -> Utility.getSafely(mixFindInfoList, 0)
                        else -> {
                            Utility.getSafely<MixFindInfo>(mixFindInfoList, position - 2)
                        }
                    }
                } else {
                    if (position == 0 || position == 1) {
                        null
                    } else {
                        Utility.getSafely<MixFindInfo>(mixFindInfoList, position - 2)
                    }
                }
            } else {
                if (position == 0) {
                    null
                } else {
                    Utility.getSafely<MixFindInfo>(mixFindInfoList, position - 1)
                }
            }
        }
    

    这种时候,每一种判断的分支对应一个值,返回命中的分支对应的值

    类型判断和类型转换

    java中类型判断和转化:

    if (holder instance of MemberVipHeaderHolder) {
        ((MemberVipHeaderHolder)holder).bindData(title, TYPE_VIP_HEADER, isVip, isBtnShow);
    }
    

    java中利用instance of进行类型判断,然后直接通过(type)instance进行类型强转,将instance转化为type类型的实例。如果instance不是type类型,那么将会抛出类型转化异常。
    而在kotlin中,可以通过as和is关键字进行类型判断和转化

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
            when (getItemViewType(position)) {
                ITEM_TYPE_ACTIVITY -> {
                    if (holder is MemberAssignViewHolder) {
                        holder.bindData(activityList)
                    }
                }
                ITEM_TYPE_MEMBER_INFO_CARD -> {
                    if (holder is MemberInfoCardViewHolder) {
                        holder.bindData()
                    }
                }
                else -> {
                    holder.itemView.setPadding(0, 0, 0, if (position == itemCount - 1) UIUtil.dp2px(40f) else 0)
                    if (holder is BaseViewHolder) {
                        holder.holderType = BaseViewHolder.TYPE_MEMBER
                    }
                    super.onBindViewHolder(holder, position)
                }
            }
        }
    

    利用 a is type 来判断a是否是type类型,如果是type类型的话,那么kotlin将会自动为我们将a转化为type类型,而不需要我们再手动的利用as进行类型转化。如果kotlin直接利用as进行类型转化的话,如果类型不一致或者a为null时,也会抛出异常,但是kotlin提供了类型不一致时的安全转化,如下

    val x: String ?= y as? String
    

    上面的例子中,as?是一个安全转化符,如果失败,将会返回null,而不会直接进行类型转化。

    lambda

    在java中,需要升级到java8才支持lambda表达,而kotlin天生支持lambda表达式,lambda表达式的完成语法如下:

    val sum = { x: Int, y: Int -> x + y }
    

    上面的例子定义了一个函数,求得sum = x + y,lambda表达式总是括在大括号中,完整的参数声明和表达式都放在大括号内,函数体在->之后,->之前都是参数,并且可能有类型标注,如果推断出该lambda的返回值类型不是Unit,也就是返回值不为空,那么该lambda主体的最后一个表达式将被视为返回值
    下面是某一个lambda的例子:

    FindExchangeManager.getInstance().loadExchangeData(mContext, findInfo.discoveryModuleId,
                        holder.adapterPosition, findInfo, FindExchangeManager.ExchangeCallback { data, position, discoveryModuleId ->
                    if (Utility.isEmpty(mContext)) {
                        return@ExchangeCallback
                    }
    
                    val info = getMixInfoFromPosition(position)
                    if (data != null && info != null) {
                        info.topics = data
                        notifyItemChanged(position)
                    }
                })
    

    如果在lambda表达式中调用return,将会直接return掉整个上层函数,因为lambda表达式只是一个代码块,虽然看起来功能像是函数,不过也不能说在lambda表达式中不能够return,只要给return指定一个标签,就会结束掉标签所指定的代码块,就像上面的例子,这个lambda表达式是在ExchangeCallback中使用的,如果要结束这个lambda,那么只需要在lambda中return代码添加上@ExchangeCallback标签就行,就绘结束掉ExchangeCallback的这个方法。

    ViewHolder中使用kotlin extension

    kotlin extension的使用需要先导入相应的xml

    //在上一篇的activity中
    import kotlinx.android.synthetic.main.activity_member_center.*
    
    //在这个viewHolder中
    import kotlinx.android.synthetic.main.listitem_assign.view.*
    

    对于这个导包操作,需要注意的是,kotlinx.android.synthetic.main是固定前缀,后面的是当前页面的layout文件名称,如果是activity,只需要再在后面添加上.,那就能够在activity中直接以id的形式调用对应的View。如果是在viewHolder中,那么还需要添加上.view,在添加.才能够使用,而且在viewHolder中,需要以其itemView为持有者来调用。

    init {
            context = itemView.context
            itemView.btn_get_gift.setOnClickListener(this)
            itemView.btn_not_gift.setOnClickListener(this)
        }
    

    如上所示,利用itemView来持有其中的view的id,并直接调用。
    这个init块,通常都是来做类的初始化的,在构造器执行完之后,会调用这个init块,我们如果有什么需要做初始化的,只需要放在这个init块中

    String 转其他类型

    java中String转long类型的话,需要调用Long.paras(string)进行转化,如果string不是相应的的类型,那么将会抛出异常。而在kotlin中也有这个方法,不过还新增了几个转化的方法:

    //如果string不是一个合法的类型,那么将会抛出异常NumberFormatException
    string.toLong()
    
    //如果string不是一个合法的类型,那么将会返回null
    string.toLongOrNull()
    

    结合rxjava

    kotlin中有专门的rx-kotlin库,刚开始并没有引入这个库,所以尝试着还是使用rxjava

    Observable.timer(getDelayTime(), TimeUnit.MILLISECONDS)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe({
                        getGrabResult(activityId, thirdId)
                    })
    

    还是一样的用法,不过好的一点是可以使用lambda表达式了,subscribe({})中间的花括号就是lambda表达式

    todo标注

    在java中,使用todo标识并不会出现什么问题,在kotlin中,一旦我们实现了接口或者抽象类,那么我们自动生成的实现方法,将会带上kotlin的todo标注

    override fun refreshServerTime(time: Long?) {
            TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
        }
    

    如果我们没有删除掉todo这行代码,那么kotlin将会直接报错,kotlin.NotImplementedError,因为这个todo是kotlin默认我们将会删除掉的,如果在运行时还存在,那么执行到todo,并不会跳过,而是会执行,然后报错。

    kotlin标注

    在kotlin中,如果需要在kotlin的高阶函数表达式返回,而不需要直接在外层函数中返回,或者嵌套循环,想从内层循环终止外层循环等,这种时候我们就需要用到kotlin的label了。比如说下面的例子:

    private fun canAutoContinue(pos: Int): Boolean {
            if (commonGoodList == null || continueGoodList == null || commonGoodList!!.size < pos) {
                return false
            }
            val commonGood: RechargeGood? = commonGoodList!![pos]
    
            continueGoodList!!.forEach {
                if (it.upRenewId == commonGood?.id) {
                    return true
                }
            }
            print("1111")
            return false
        }
    

    上面的在forEach中,return的执行将会直接将会结束掉canAutoContinue方法,如果我们只想结束掉forEach,我们可以像下面这样做。

    private fun canAutoContinue(pos: Int): Boolean {
            if (commonGoodList == null || continueGoodList == null || commonGoodList!!.size < pos) {
                return false
            }
            val commonGood: RechargeGood? = commonGoodList!![pos]
    
            continueGoodList!!.forEach lit@{
                if (it.upRenewId == commonGood?.id) {
                    return@lit
                }
            }
            print("1111")
            return false
        }
    

    如上面的lit label,那么这样return将会结束lit所指定的lable循环,从而继续往下执行。
    比如说for循环嵌套

    outFor@ for(x in 1..5) {
                for (y in 1..6) {
                    if(x == y) {
                        break@outFor
                    }
                }
            }
    

    一旦满足了x==y,然么将会直接跳出外层的for循环,否则一般的break,只会中断内层的for循环。要为一个表达式加标签,我们只要在其前加标签即可。
    在返回和跳转语句中,可以指定标签来表示结束哪个标签对应的代码段
    如果需要在lambda表达式

    kotlin 定义属性时的get和set方法

    kotlin定义属性时,可以同时定义get和set方法,而不需要再重新定义方法去赋值。

    private var delayTime: Long
            private set(value) {
                delayTime = value
            }
            get() {
                return KKConfigManager.getInstance().
                        getConfig(KKConfigManager.ConfigType.GET_RECHARGE_ORDER_DELAY)
                        .toLongOrNull() ?: 1000
            }
    

    而且,可以添加上访问限定符。而这个get和set方法的设置,主要是通过代码中的位置来标示修饰的哪个变量。

    kotlin的幕后字段

    Kotlin 中的类不能有字段, 然而我们在有时在自定义访问器时,也就是get和set方法时,需要又一个幕后字段,这个字段是kotlin提供的,field,我们可以直接使用。

    var counter = 0 // 注意:这个初始器直接为幕后字段赋值 set(value) {
    if (value >= 0) field = value }
    
    
    val nameHash:Int = 3
            get() {
                field = 5
                return 10
        }
    

    有可能我们需要临时缓存变量的值,因为可能其变量的值可能和返回的值不一致,所以一旦我们需要知道变量的值,我们便可以通过幕后字段来访问。

    kotlin的幕后属性

    幕后属性主要用于外部只能读,内部可以读写的需求下出现的。例如下面的例子:

    val counter get() = _counter
    private var _counter = 0
    

    这个_counter就是counter的幕后属性,外部只能访问counter,不能访问_counter,而counter的值又是_counter来指定的。

    kotlin的扩展

    在kotlin中,if else的三目运算符如下所示:

    if(a) x else y
    

    而我们也能通过扩展函数来扩展boolean

    inline fun <T> Boolean.yes(action: () -> T): Boolean {
        if (this) action()
        return this
    }
    
    inline fun <T> Boolean.no(action: () -> T): Boolean {
        if (!this) action()
        return this
    
    

    这样,我们在kt文件中,只要引入相应的类,定义在文件内,而非class下

    import com.kuaikan.community.extend.yes
    

    扩展函数是静态解析的,所以不能够进行重载,这就意味着我们,我们不能重载类原有的方法,比如说collection的add方法等..
    对于扩展函数还有一个非常好用的东西,被扩展的类型可以为空,被称为可空接收者

    fun Any?.toString(): String {
    if (this == null) return "null"
    // 空检测之后,“this”会自动转换为非空类型,所以下面的 toString() // 解析为 Any 类的成员函数
    return toString()
    }
    

    kotlin的扩展属性

    既然kotlin支持扩展函数,当然也支持扩展属性了。

     val <T> List<T>.lastIndex: Int
        get() = size - 1
    

    这样就给了List的一个属性lastIndex

    kotlin的范型

    关于out和in这两个关键字,out是用来输出的,也就是生产者,所以只能作为返回类型,相当于java中的extends,用来界定类型上限;in是用来输入的,所以只能作为消费类型,in类似于java中的super,用来界定类型下限

    kotlin的单例

    在kotlin中,声明单例变得非常简单,只需要一个关键字object用来修饰对象,就像变量声明一样,对象声明不是一个表达式,不能用在赋值语句右边,对象的初始化是线程安全的。如果需要引用该对象,我们只需要通过其名称来调用。

    object DataProviderManager {
    fun registerDataProvider(provider: DataProvider) {
    // ......
    }
    val allDataProviders: Collection<DataProvider> get() = // ......
    }
    
    //调用
    DataProviderManager.registerDataProvider(......)
    

    伴生对象

    类内部的伴生对象的声明如下:

    class MyClass { 
        companion object { }
    }
    val x = MyClass.Companion
    

    请注意,即使伴生对象的成员看起来像其他语言的静态成员,在运行时他们仍然是真实对象的实例成员,而且,例如还可以实现接口:

    interface Factory<T> { fun create(): T
    }
    class MyClass {
    companion object : Factory<MyClass> {
    override fun create(): MyClass = MyClass() }
    }
    

    如果我们想将伴生对象的成员生成为真正的静态方法和字段,我们我们可以使用@JvmStatic注解

    对象表达式和对象声明之间的语义差异

    对象表达式和对象声明之间有一个重要的语义差别:

    • 对象表达式是在使用他们的地方立即执行(及初始化)的;
    • 对象声明是在第一次被访问到时延迟初始化的;
    • 伴生对象的初始化是在相应的类被加载(解析)时,与 Java 静态初始化器的语义相匹配。

    onAttachedToWindow和onDetachedFromWindow

    这个是无关kotlin的,只是刚好在写kt代码时遇到的一个问题,
    关于onAttachedToWindow和onDetachedFromWindow的触发时机,

    onAttachedToWindow就是在这个View被添加到Window时触发的

    顾名思义,onAttachedToWindow就是在这个View被添加到Window时触发的,通过了dispatchAttachedToWindow这个方法触发的,那么View被添加到window的时机又是什么,其实就是对应的activity在生命周期onResume的时候调用的,activity对应的view在onResume的时候被添加添加到window。且每一个view都只会被调用一次,父view调用在前,不论view的visibility状态都会被调用,适合做些view特定的初始化操作;

    onDetachedFromWindow

    onDetachedFromWindow方法是在activity的生命周期destroy的时候被调用的,也就是act对应的view从window中移除的时候,且每个view只会被调用一次,父view的调用在后,也不论view的visibility状态都会被调用,适合做最后的清理操作;

    kotlin类的继承性

    java的class默认都是可以继承的,只有在声明为final的class才是不可继承的。而kotlin却是相反的,默认的类都是不可继承的,也就是默认的修饰符为final,只有显式的声明为open的类才是可以继承的。而对于抽象类,java和kotlin默认都是可以继承的,但是子类必须是抽象类或者实现了该类的所有抽象方法。

    kotlin扩展函数的java调用方式

    kotlin中的扩展函数是非常方便的,刚开始以为在java中不能够调用到kotlin中的扩展函数,后面发现不是的,对于我们定义的扩展函数类,在java中有相应的使用方式

    //KotlinExt.kt
    fun Int.dp2px(context: Context?): Int {
        val scale = context?.resources?.displayMetrics?.density?.toInt()
        return (this * (scale ?: 2) + 0.5f).toInt()
    }
    //java中调用
    KotlinExtKt.dp2px(2, getContext());
    

    我们只需要import相应的kotlinExt文件,然后就可以调用其中的方法,但使用方式和kotlin有不一致的地方,对于kt的扩展函数的接收者,也就是上面的Int,在java中会被认为是第一个入参, 如果扩展函数接受的是普通参数,那么这个参数就直接作为后续的入参。
    如果扩展数据的第二个参数是lambda表达式,那么有java相应的生成方式:

    //KotlinExt.kt
    inline fun <T> Boolean.yes(action: () -> T): Boolean {
        if (this) action()
        return this
    }
    //java中调用
    KotlinExtKt.yes(true, new Function0<Object>() {
                        @Override
                        public Object invoke() {
                            return null;
                        }
                    });
    

    上面的Boolean的扩展yes方法,接收一个高阶函数,kt中用lambda表示,而在java中,需要以一个匿名内部类的方式替代,

    kotlin中内部类

    在java中,如果定义一个非静态类内部类,默认将会持有外部类的引用,经常会造成外部类无法被回收,导致内存泄漏,而kotlin中,内部类默认不会持有外部类的引用,只有添加上inner关键字修饰,才会持有外部引用。

    kotlin 密封类

    kotlin的密封类是java所没有的,在kotlin中,如果一个类被标明为密封类,那么其所有的子类都需要在父类中列出,作为密封类的嵌套内部类

    kotlin中的惰性集合操作

    kotlin中一些集合操作,比如说map和filter,对于我们来说是非常方便的,比如说筛选一个人群的年龄大于30的人的名称。

    people.map(it.name).filter{it.age > 30}
    

    但是上面这种方式,在链式调用的链足够长时,将会产生性能问题,因为链式调用的每一个步骤都会创建一个中间集合,用来存储中间结果,也就是说,每一个步骤的结果都存储在另外一个变量中。所以,kotlin给我们提供了另外一种使用方式,来避免创建中间对象。也就是惰性集合。sequencez作为惰性集合操作,可以优化map和filter等操作,将不会生成任何中间对象

    people.asSequence().map(people.name).filter(it.startWith("a")).toList()
    

    sequencez会将元数据生成一个流式对象,对于集合的每一个元素利用iterator遍历,执行map和filter,筛选出符合条件的数据。
    惰性集合的操作包含了中间操作和末端操作,中间操作就是转换操作,末端操作就是筛选操作,在上面的例子里面,map是中间操作,filter是末端操作,如果没有末端操作,那么,中间操作也不会执行,既然这样,如果我们末端操作停止了,中间操作也会停止,也就是说,并不会保证所有的中间操作都能够执行。

    butterknife库的使用

    在java中,butterknife库是一个众所周知的代替findViewId操作的库,如果我们直接在kotlin中想要使用butterknife这个库,那还是需要一些额外的操作的。

    引入KotterKnife或者自己提供extension

    这个是butterknife作者提供的另外一个kotlin版本的替代库。使用方式如下:

    val updateTime by bind<TextView>(R.id.update_time)
    

    虽然我没看KotterKnife内部实现,但是应该跟我们自己提供扩展函数的实现方式是一样的,我们可以通过自己提供扩展函数来设置做这些操作:

    fun <T : View> Activity.bind(@IdRes idRes: Int): Lazy<T> {
        return lazy { findViewById<T>(idRes) }
    }
    
    fun <T : View> View.bind(@IdRes idRes: Int): Lazy<T> {
        return lazy { findViewById<T>(idRes) }
    }
    
    fun <T : View> android.support.v7.widget.RecyclerView.ViewHolder.bind(@IdRes idRes: Int): Lazy<T> {
        return lazy { this.itemView.findViewById<T>(idRes) }
    }
    
    fun <T : View> Fragment.bind(@IdRes idRes: Int): Lazy<T?> {
        return lazy { this.view?.findViewById<T>(idRes) }
    }
    

    这个扩展数据其实就是代替我们执行findViewById操作,虽然我们是在初始化的时候定义了View的对象,但是它并不会马上执行,lazy关键字修饰的对象将会等到使用时再执行相应方法。也就是说,这个findViewById将会在setContentView之后执行,因为我们使用这个对象肯定是在其之后的。

    仍然使用butterknife

    其实在kotlin中,我们还是可以使用butterknife的,只是我们需要将butterknife的注解解释器替换为kotlin annotation Processor tool,这样,在kt文件中就能够使用butterknife的注解了,我们不需要担心会对之前的注解产生影响,因为kapt是兼容了annotationProcessor的。

    annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
    ====>
    kapt 'com.jakewharton:butterknife-compiler:8.4.0'
    

    替换了kapt之后,就可以进行第二步了,在kt中利用laterinit修饰对象,来保证view对象可以在通过butterknife库赋值

     @BindView(R.id.user_v_layout)
        lateinit var vLayout: ImageView
        @BindView(R.id.comment_user_icon)
        lateinit var userIconIV: ImageView
        @BindView(R.id.comment_user_name)
        lateinit var userNameTV: TextView
    

    相关文章

      网友评论

          本文标题:kotlin学习记录

          本文链接:https://www.haomeiwen.com/subject/hbawfqtx.html