你应该知道的kotlin实用技巧

前言 众所周知,kotlin是google力推的用以取代java的android开发语言
kotlin使用起来比较方便,同时有许多语法糖
本文主要讲解了一些比较实用的kotlin技巧
自定义圆角矩形 【你应该知道的kotlin实用技巧】在项目中,我们常常要定义圆角矩形背景,一般是用自定义drawable实现的
但是圆角矩形的背景与圆角常常会有细微的变化,而一旦变化我们又要新创建一个drawable文件
这样就会导致文件爆炸的问题
我们可以利用kotlin的扩展函数,来实现简单方便的圆角矩形背景

fun View.setRoundRectBg(color: Int = Color.WHITE, cornerRadius: Int = 15.dp) { background = GradientDrawable().apply { setColor(color) setCornerRadius(cornerRadius.toFloat()) } }

对于需要自定义背景的View,直接调用setRoundRectBg即可,简单方便
reified使用 reified,kotlin中的泛型实化关键字,使抽象的东西更加具体或真实。
我们举两个例子来看看怎么使用reified
startActivity例子
我们一般startActivity是这样写的
startActivity(context, NewActivity::class.java)

我们利用reified定义一个扩展函数
// Function inline fun Activity.startActivity(context: Context) { startActivity(Intent(context, T::class.java)) }// Caller startActivity(context)

使用 reified,通过添加类型传递简化泛型参数
这样就不用手动传泛型的类型过去了
Gson解析例子
我们首先看下一般我们使用gson解析json是怎么做的
在Java序列化库(如Gson)中,当您想要反序列化该JSON字符串时,您最终必须将Class对象作为参数传递,以便Gson知道您想要的类型。
User user = new Gson().fromJson(getJson(), User.class)

现在,让我们一起展示reified类型实化参数的魔法 我们将创建一个非常轻量级的扩展函数来包装Gson方法:
inline fun Gson.fromJson(json: String) = fromJson(json, T::class.java)

现在,在我们的Kotlin代码中,我们可以反序列化JSON字符串,甚至根本不需要传递类型信息!
val user: User = Gson().fromJson(json)

Kotlin根据它的用法推断出类型 - 因为我们将它分配给User类型的变量,Kotlin使用它作为fromJson()的类型参数
kotin接口支持SAM转换 什么是SAM转换?可能有的同学还不太了解,这里先科普一下:
SAM 转换,即 Single Abstract Method Conversions,就是对于只有单个非默认抽象方法接口的转换 —— 对于符合这个条件的接口(称之为 SAM Type ),在 Kotlin 中可以直接用 Lambda 来表示 —— 当然前提是 Lambda 的所表示函数类型能够跟接口的中方法相匹配。
在Kotlin1.4之前,Kotlin是不支持Kotlin的SAM转换的,只支持Java SAM转换,官方给出的的解释是:是 Kotlin 本身已经有了函数类型和高阶函数,不需要在去SAM转化。 这个解释开发者并不买账,如果你用过Java Lambda和Fuction Interface。当你切换到Kotlin时,就会很懵逼。看来Kotlin是意识到了这个,或者是看到开发者的反馈,终于支持了。
在1.4之前,只能传递一个对象,是不支持Kotlin SAM的,而在1.4之后,可以支持Kotlin SAM,但是用法有一丢丢变化。interface需要使用fun关键字声明。使用fun关键字标记接口后,只要将此类接口作为参数,就可以将lambda作为参数传递。
// 注意需用fun 关键字声明 fun interface Action { fun run() }fun runAction(a: Action) = a.run()fun main(){ // 1.4之前,只能使用object runAction(object : Action{ override fun run() { println("run action") } }) // 1.4-M1支持SAM,OK runAction { println("Hello, Kotlin 1.4!") } }

委托 有时候,完成一些工作的方法是将它们委托给别人。这里不是在建议您将自己的工作委托给朋友去做,而是在说将一个对象的工作委托给另一个对象。
当然,委托在软件行业不是什么新鲜名词。委托 (Delegation) 是一种设计模式,在该模式中,对象会委托一个助手 (helper) 对象来处理请求,这个助手对象被称为代理。代理负责代表原始对象处理请求,并使结果可用于原始对象。
类委托
举个例子,当我们要实现一个增强版的ArrayList,支持恢复最后一次删除的item
实现这个用例的一种方式,是继承 ArrayList 类。由于新的类继承了具体的 ArrayList 类而不是实现 MutableList 接口,因此它与 ArrayList 的实现高度耦合。
如果只需要覆盖 remove() 函数来保持对已删除项目的引用,并将 MutableList 的其余空实现委托给其他对象,那该有多好啊。为了实现这一目标,Kotlin 提供了一种将大部分工作委托给一个内部 ArrayList 实例并且可以自定义其行为的方式,并为此引入了一个新的关键字: by。
class ListWithTrash (private val innerList: MutableList = ArrayList()) : MutableCollection by innerList { var deletedItem : T? = null override fun remove(element: T): Boolean { deletedItem = element return innerList.remove(element) } fun recover(): T? { return deletedItem } }

by 关键字告诉 Kotlin 将 MutableList 接口的功能委托给一个名为 innerList 的内部 ArrayList。通过桥接到内部 ArrayList 对象方法的方式,ListWithTrash 仍然支持 MutableList 接口中的所有函数。与此同时,现在您可以添加自己的行为了。
属性委托
除了类代理,您还可以使用 by 关键字进行属性代理。通过使用属性代理,代理类会负责处理对应属性 get 与 set 函数的调用。这一特性在您需要在其他对象间复用 getter/setter 逻辑时十分有用,同时也能让您可以轻松地对简单支持字段的功能进行扩展
举个例子,利用委托属性可以封装SharedPreference
将数据存储操作委托给代理类有几个好处
1.则精简了代码,方便了存储与读取调用
2.与SP进行了解耦,后续如果要替换存储库,只需要修改代理类即可
调用如下:
object Pref: PreferenceHolder() { var isFirstInstall: Boolean by bindToPreferenceField(false) var time: Long? by bindToPreferenceFieldNullable() }

具体实现可见:SharedPreferences用Kotlin应该这样写
带状态的LiveData 目前我们在开发的过程中越来越多的使用MVVM模式与ViewModel
我们也常常用LiveData来标识网络请求状态
我们需要定义请求开始,请求成功,请求失败,三个LiveData
这其实也是很冗余重复的代码,因此我们可以进行一定的封装,封装一个带状态的LiveData
定义如下:
typealias StatefulLiveData = LiveData> typealias StatefulMutableLiveData = MutableLiveData>@MainThread inline fun StatefulLiveData.observeState( owner: LifecycleOwner, init: ResultBuilder.() -> Unit ) { val result = ResultBuilder().apply(init)observe(owner) { state -> when (state) { is RequestState.Loading -> result.onLading.invoke() is RequestState.Success -> result.onSuccess(state.data) is RequestState.Error -> result.onError(state.error) } } }

使用如下
val data = https://www.it610.com/article/StatefulMutableLiveData() viewModel.data.observeState(viewLifecycleOwner) { onLading = { //loading } onSuccess = { data -> //success } onError = { exception -> //error } }

通过以上封装,可以比较优雅简洁的封装网络请求的loading,success,error状态,精简了代码,结构也比较清晰
DSL DSL(domain specific language),即领域专用语言:专门解决某一特定问题的计算机语言,比如大家耳熟能详的 SQL 和正则表达式。
但是,如果为解决某一特定领域问题就创建一套独立的语言,开发成本和学习成本都很高,因此便有了内部 DSL 的概念。所谓内部 DSL,便是使用通用编程语言来构建 DSL。比如,本文提到的 Kotlin DSL,我们为 Kotlin DSL 做一个简单的定义:
“使用 Kotlin 语言开发的,解决特定领域问题,具备独特代码结构的 API 。”
举个例子,我们使用TabLayout时,如果要为他添加监听,需要实现以下3个方法
override fun onTabReselected(tab: TabLayout.Tab?){}override fun onTabUnselected(tab: TabLayout.Tab?){}override fun onTabSelected(tab: TabLayout.Tab?){}

其实我们一般只会用到onTabSelected方法,其余两个一般是空实现
我们利用DSL对OnTabSelectedListener进行封装,即可避免写不必要的空实现代码
具体实现如下:
private typealias OnTabCallback = (tab: TabLayout.Tab?) -> Unitclass OnTabSelectedListenerBuilder : TabLayout.OnTabSelectedListener {private var onTabReselectedCallback: OnTabCallback? = null private var onTabUnselectedCallback: OnTabCallback? = null private var onTabSelectedCallback: OnTabCallback? = nulloverride fun onTabReselected(tab: TabLayout.Tab?) = onTabReselectedCallback?.invoke(tab) ?: Unitoverride fun onTabUnselected(tab: TabLayout.Tab?) = onTabUnselectedCallback?.invoke(tab) ?: Unitoverride fun onTabSelected(tab: TabLayout.Tab?) = onTabSelectedCallback?.invoke(tab) ?: Unitfun onTabReselected(callback: OnTabCallback) { onTabReselectedCallback = callback }fun onTabUnselected(callback: OnTabCallback) { onTabUnselectedCallback = callback }fun onTabSelected(callback: OnTabCallback) { onTabSelectedCallback = callback }}fun registerOnTabSelectedListener(function: OnTabSelectedListenerBuilder.() -> Unit) = OnTabSelectedListenerBuilder().also(function)

定义DSL的一般步骤:
  • 1.先定义一个类去实现回调接口,并且实现它的回调方法。
  • 2.观察回调方法的参数,提取成一个函数类型(function type),并且按照需要使用类型别名给函数类型起一个别称,并且用私有修饰。
  • 3.在类里面声明一些可空的函数类型的可变(var)私有成员变量,并且在回调函数中拿到对应的变量实现它的invoke函数,传入对应的参数。
  • 4.在类中定义一些跟回调接口一样名字,但是参数是对应的函数类型的函数,并且将函数类型赋值给当前类的对应的成员变量。
  • 5.定义一个成员函数,参数是一个带有我们定好那个类的接受者对象并且返回Unit的Lambda表达式,在函数里创建相应的对象,并且使用also函数把Lambda表达式传进去。
调用如下:
tabLayout.addOnTabSelectedListener(registerOnTabSelectedListener { onTabSelected { vpOrder.currentItem = it?.position ?: 0 } })

如上,就可以避免写一些不必要的空实现代码了
学习资源推荐 这本阿里开源的《kotlin从入门到精通》帮助你更好的学习kotlin!!! 附上资料截图:
你应该知道的kotlin实用技巧
文章图片

1.准备开始
  • 基本语法
你应该知道的kotlin实用技巧
文章图片

  • 习惯用语
  • 编码风格
2.基础
  • 基本类型
你应该知道的kotlin实用技巧
文章图片

  • 控制流
  • 返回与跳转
3.类和对象
  • 类和继承
  • 属性和字段
  • 接口
  • 可见性修饰词
  • 扩展
  • 数据对象
  • 泛型
你应该知道的kotlin实用技巧
文章图片

  • 嵌套类
  • 枚举类
  • 对象表达式和声明
  • 代理模式
  • 代理属性
4.函数和lambda表达式
  • 函数
你应该知道的kotlin实用技巧
文章图片

  • 高级函数和lambda表达式
  • 内联函数
5.其他
  • 多重申明
  • Ranges
  • 类型检查和自动转换
你应该知道的kotlin实用技巧
文章图片

  • This表达式
  • 等式
  • 运算符重载
  • 空安全
  • 异常
  • 注解
  • 反射
  • 动态类型
6.参考
  • 互动性
7.工具
  • Kotlin代码文档
  • 使用Maven
  • 使用Ant
你应该知道的kotlin实用技巧
文章图片

  • 使用Griffon
  • 使用Gradle
8.FAQ
  • 与 java 对比
  • 与 Scala 对比
你应该知道的kotlin实用技巧
文章图片

这份完整版的《kotlin从入门到精通》PDF版电子书,朋友们如果需要可以 私信 或者 评论 888,我免费分享给你。
相关视频: 【2021最新版】Android studio安装教程+Android(安卓)零基础教程视频(适合Android 0基础,Android初学入门)含音视频_哔哩哔哩_bilibili
Android进阶学习:Kotlin核心技术_哔哩哔哩_bilibili
【 Android进阶教程】——热修复原理解析_哔哩哔哩_bilibili
【 Android进阶教程】——如何解决OOM问题与LeakCanary原理解析_哔哩哔哩_bilibili

    推荐阅读