共计 6512 个字符,预计需要花费 17 分钟才能阅读完成。
前言
家喻户晓,kotlin 是 google 力推的用以取代 java 的 android 开发语言
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 <reified T : Activity> Activity.startActivity(context: Context) {startActivity(Intent(context, T::class.java))
}
// Caller
startActivity<NewActivity>(context)
应用 reified,通过增加类型传递简化泛型参数
这样就不必手动传泛型的类型过来了
Gson 解析例子
咱们首先看下个别咱们应用 gson 解析 json 是怎么做的
在 Java 序列化库(如 Gson)中,当您想要反序列化该 JSON 字符串时,您最终必须将 Class 对象作为参数传递,以便 Gson 晓得您想要的类型。
User user = new Gson().fromJson(getJson(), User.class)
当初,让咱们一起展现 reified 类型实化参数的魔法 咱们将创立一个十分轻量级的扩大函数来包装 Gson 办法:
inline fun <reified T> 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。
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
class ListWithTrash <T>(private val innerList: MutableList<T> = ArrayList<T>()) : MutableCollection<T> 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<T> = LiveData<RequestState<T>>
typealias StatefulMutableLiveData<T> = MutableLiveData<RequestState<T>>
@MainThread
inline fun <T> StatefulLiveData<T>.observeState(
owner: LifecycleOwner,
init: ResultBuilder<T>.() -> Unit) {val result = ResultBuilder<T>().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 = StatefulMutableLiveData<String>()
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?) -> Unit
class OnTabSelectedListenerBuilder : TabLayout.OnTabSelectedListener {
private var onTabReselectedCallback: OnTabCallback? = null
private var onTabUnselectedCallback: OnTabCallback? = null
private var onTabSelectedCallback: OnTabCallback? = null
override fun onTabReselected(tab: TabLayout.Tab?) =
onTabReselectedCallback?.invoke(tab) ?: Unit
override fun onTabUnselected(tab: TabLayout.Tab?) =
onTabUnselectedCallback?.invoke(tab) ?: Unit
override fun onTabSelected(tab: TabLayout.Tab?) =
onTabSelectedCallback?.invoke(tab) ?: Unit
fun 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!!!
附上材料截图:
1. 筹备开始
- 根本语法
- 习惯用语
- 编码格调
2. 根底
- 根本类型
- 包
- 控制流
- 返回与跳转
3. 类和对象
- 类和继承
- 属性和字段
- 接口
- 可见性修饰词
- 扩大
- 数据对象
- 泛型
- 嵌套类
- 枚举类
- 对象表达式和申明
- 代理模式
- 代理属性
4. 函数和 lambda 表达式
- 函数
- 高级函数和 lambda 表达式
- 内联函数
5. 其余
- 多重申明
- Ranges
- 类型检查和主动转换
- This 表达式
- 等式
- 运算符重载
- 空平安
- 异样
- 注解
- 反射
- 动静类型
6. 参考
- 互动性
7. 工具
- Kotlin 代码文档
- 应用 Maven
- 应用 Ant
- 应用 Griffon
- 应用 Gradle
8.FAQ
- 与 java 比照
- 与 Scala 比照
这份完整版的《kotlin 从入门到精通》PDF 版电子书,敌人们如果须要能够 私信 或者 评论 888,我收费分享给你。
相干视频:
【2021 最新版】Android studio 装置教程 +Android(安卓)零基础教程视频(适宜 Android 0 根底,Android 初学入门)含音视频_哔哩哔哩_bilibili
Android 进阶学习:Kotlin 核心技术_哔哩哔哩_bilibili
【Android 进阶教程】——热修复原理解析_哔哩哔哩_bilibili
【Android 进阶教程】——如何解决 OOM 问题与 LeakCanary 原理解析_哔哩哔哩_bilibili