Kotlin:延迟初始化和密封类

kotlin&android.png

前言

什么?我屮艸芔茻,兄弟们竟然把我前两篇写的变量和函数、标准函数和静态方法给看完了,欢迎打怪升级来到我的第三篇关于Kotlin输出文章,想必前两篇文章已经让你已经都掌握了~😬😬😬😬😬😬😬😬😬😬
下面有请各位大佬观看通俗易懂Kotlin系列之第三篇文章——延迟初始化和密封类
发车了兄弟们GO GO GO ~ 😬

1:延时初始化

首先我给出一段代码

    class MainActivity : AppCompatActivity(), View.OnClickListener {
        private var adapter: Adapter? = null
        override fun onCreate(savedInstanceState: Bundle?) {
            ...
            adapter = Adapter(list)
            ...
        }
        override fun onClick(v: View?) {
            ...
            adapter?.todoSomeThing(list.size - 1)
            ...
        }
    }

上述代码中adapter是一个全局变量,由于需要在onCreate中初始化才能使用,所以不得不先将adapter赋值为null,同时把它的类型声明成MsgAdapter?,并且最操蛋的问题是我必须在任何一个使用的地方?.对adapter进行判空,那么一旦我们的项目中使用过多的全局变量是不是我们需要在任何一个调用的地方都进行判空? 就这?啊?就这?Kotlin就这?NO NO NO 当然不会,Kotlin当然给出了解决方式——使用是 lateinit关键字对全局变量进行延时初始化 ,意为告诉Kotlin编译器我会晚些时候对这个变量进行初始化,这样就不用再一开始的时候初始化为null了~!
实现代码如下:

    class MainActivity : AppCompatActivity(), View.OnClickListener {
        private lateinit var adapter:Adapter
        override fun onCreate(savedInstanceState: Bundle?) {
            ...
            adapter = Adapter(List)
            ...
        }
        override fun onClick(v: View?) {
            ...
            adapter.todoSomeThing(List.size - 1)
            ...
        }
    }

请注意再使用lateinit关键字的时候一定要确保你的对象已经被初始化了,否则就会抛出UninitializedPropertyAccessException异常
image.png

另外我们可以对对象判断是否进行了初始化,这样就可以避免重复初始化对象

    if (!::adapter.isInitialized) {
        adapter = MsgAdapter(msgList)
    }

2:使用密封类优化代码

他也可以帮助我们写出更加安全规范的代码,通常来说密封类可以结合RecyclerView适配器中的ViewHolder一起使用
下边我们会为了实现如下效果编写一个简单的RecyclerView聊天界面


image.png

代码如下

class MesActivity : AppCompatActivity(), View.OnClickListener {
    private val mesDatas = ArrayList<MesData>()
    private lateinit var mesAdapter: MesAdapter
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_mes)
        initMes();
        val layoutManager = LinearLayoutManager(this)
        recycler_view.layoutManager = layoutManager
        if (!::mesAdapter.isLateinit) {
            mesAdapter = MesAdapter(mesDatas)
        }
        recycler_view.adapter = mesAdapter
        send_mes_btn.setOnClickListener(this)

    }

    private fun initMes() {
        mesDatas.add(MesData("第一次听这首歌我初三,现在我都快30了,人在变,但是这首歌给我的感觉还是那么温暖", MesData.TYPE_LEFT))
        mesDatas.add(
            MesData(
                "周华健的声音沧桑却也潇洒,还不失温暖。最早对于流行乐的印象应该是很小的时候老爸对我唱他的《亲亲我的宝贝》",
                MesData.TYPE_RIGHT
            )
        )
        mesDatas.add(MesData("想着有机会一定要带老爸去听一次他的现场。", MesData.TYPE_LEFT))
        mesDatas.add(MesData("我曾经去过", MesData.TYPE_RIGHT))
        mesDatas.add(MesData("经典就是多年后再听到这首歌还是会忍不住单曲循环", MesData.TYPE_LEFT))

    }

    override fun onClick(v: View?) {
        when (v?.id) {
            R.id.send_mes_btn -> sendMes()
        }
    }
    /**
     * 发送数据
     */
    private fun sendMes() {
        if (input_mes_ed.text.toString().isNotEmpty()) {
            mesDatas.add(MesData(input_mes_ed.text.toString().trim(), 1))
            mesAdapter.notifyItemInserted(mesDatas.size - 1)
            recycler_view.scrollToPosition(mesDatas.size - 1)
            input_mes_ed.setText("")
        }

    }
}
class MesAdapter(private val mesList: List<MesData>) :
    RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    inner class LeftViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val leftTv: TextView = view.findViewById(R.id.left_item_tv)
    }

    inner class RightViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val rightTv: TextView = view.findViewById(R.id.right_item_tv)
    }


    override fun getItemViewType(position: Int): Int {
        return mesList[position].type
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = if (viewType == TYPE_LEFT) {
        val view: View = LayoutInflater.from(parent.context)
            .inflate(R.layout.left_mes_layout, parent, false)
        LeftViewHolder(view)
    } else {
        val view: View = LayoutInflater.from(parent.context)
            .inflate(R.layout.right_mes_layout, parent, false)
        RightViewHolder(view)
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        val mes = mesList[position]
        when (holder) {
            is RightViewHolder -> holder.rightTv.text = mes.content
            is LeftViewHolder -> holder.leftTv.text = mes.content
            else -> throw IllegalArgumentException()
        }
    }

    override fun getItemCount() = mesList.size
}

请注意上边代码的两个Viewhodler, Kotlin中使用inner class关键字来定义内部类

data class MesData(val content: String, val type: Int) {
    companion object {
        const val TYPE_LEFT = 0
        const val TYPE_RIGHT = 1
    }
}

请注意上边代码,Koltin中 定义常量的关键字是const,注意只有在单例类、companion object或顶层方法中才可以使用const关键字
xml代码和.9图片我就不放了,这里默认为阅读者都为中级以上开发人员,我们只关注Kotlin的代码

ok实现了以上代码之后我们基本可以复现类似微信的对话形式
下边我们看一下怎么使用密封类
首先比那些如下代码

interface Result
class Success(val mes: String) : Result
class Failure(val err: Exception) : Result

创建一个接口,并且两个类success和failure分别实现result接口
接下来我们调用Result接口:

fun getResultData(result: Result) = when (result) {
    is Success -> println("isSuccess")
    is Failure -> println("isFailure")
    else ->throw IllegalArgumentException()
}
没有编写else的效果.png

可以看到我们在接受Result接口的时候必须编写 一个else 这但是我们的代码并没有第三中可能性所以我们在这里直接抛出了一个异常,只是为了满足Kotlin编译器的语法检查而已。
另外,编写else条件还有一个潜在的风险。如果我们现在新增了一个新类并实现Result接口,用于表示未知的执行结果,但是忘记在getResultMsg()方法中添加相应的条件分支,编译器在这种情况下是不会提醒我们的,而是会在运行的时候进入else条件里面,从而出现问题。

如果使用Kotlin密封类就可以解决上边的问题
密封类的关键字是sealed class,它的用法同样非常简单,我们将Result接口改造成密封类的写法:

sealed class Result
class Success(val mes: String) : Result()
class Failure(val err: Exception) : Result()

看一下没有编写Failure的情况
没有编写Failure的情况.png

编译器会直接报错 按下option+回车 直接可以提示我们添加没有写入的分支

fun getResultData(result: Result) = when (result) {
    is Success -> println("isSuccess")
    is Failure -> TODO()
}

when语句在传入一个密封类变量作为条件的时候,编译器会自动检查这个密封类有哪些分支,有没有分支没有实现,而且强制你来处理每一个分支,这样我们在添加其他分支的时候就不会丢下任何一个,也不会出现问题

请注意:密封类必须及其所有的子类都必须定义在顶层文件中

上边说过密封类可以结合RecyclerView适配器中的ViewHolder一起使用,
可以看到我们的adapter中就存在一个else语句

新建一个MesViewHodler类

sealed class MesViewHodler(view: View) : RecyclerView.ViewHolder(view)

class leftMesViewHolders(view: View) : MesViewHodler(view) {
    val leftTv: TextView = view.findViewById(R.id.left_item_tv)
}

class rightMesViewHolders(view: View) : MesViewHodler(view) {
    val rightTv: TextView = view.findViewById(R.id.right_item_tv)
}

然后修改一下adapter相关的代码

class MesAdapter(private val mesList: List<MesData>) :
    RecyclerView.Adapter<MesViewHodler>() {

    override fun getItemViewType(position: Int): Int {
        return mesList[position].type
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = if (viewType == TYPE_LEFT) {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.left_mes_layout, parent, false)
        leftMesViewHolders(view)
    } else {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.right_mes_layout, parent, false)
        rightMesViewHolders(view)
    }

    override fun onBindViewHolder(holder: MesViewHodler, position: Int) {
        val mes = mesList[position]
        when (holder) {
            is rightMesViewHolders -> holder.rightTv.text = mes.content
            is leftMesViewHolders -> holder.leftTv.text = mes.content
        }
    }

    override fun getItemCount() = mesList.size
}

我们将之前的Recyclerview.ViewHolder 改成MesViewHodler,另外在when语句中只需要处理相关的分支就可以了,else就可以不用处理了

延迟初始化和密封类到此结束了,不知道你有没有掌握牢固呢
点赞双击评论666
下篇文章来学习扩展函数和运算符重载啦~~~
有什么问题欢迎留言指出😜😜😜😜😜😜😜😜😜😜

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容