1、无需 findViewById
在布局中定义
1 | <TextView |
直接设置 TextView 的文本
1 | tv_content.text = "改变文本" |
2、Lambda
1 | tv_content.setOnClickListener(View.OnClickListener( |
可以写成下面
1 | tv_content.setOnClickListener { v -> v.visibility = View.GONE } |
3、函数变量
在 kotlin 语法中函数是可以作为变量进行传递的
1 | var result = fun(number1 : Int, number2 : Int) : Int { |
使用这个函数变量
1 | println(result(1, 2)) |
4、空安全
在 Java 不用强制我们处理空对象,所以常常会导致 NullPointerException 空指针出现,现在 Kotlin 对空对象进行了限定,必须在编译时处理对象是否为空的情况,不然会编译不通过.
在对象不可空的情况下,可以直接使用这个对象.
1 | fun getText() : String { |
在对象可空的情况下,必须要判断对象是否为空
1 | fun getText() : String? { |
如果不想判断是否为空,可以直接这样,如果 text 对象为空,则会报空指针异常,一般情况下不推荐这样使用
1 | val text = getText() |
还有一种更好的处理方式,如果 text 对象为空则不会报错,但是 text.length 的结果会等于 null
1 | val text = getText() |
5、方法支持添加默认函数
在 Java 上,我们可能会为了扩展某个方法而进行多次重载
1 | public void toast(String text) { |
1 | toast("弹个吐司"); |
但是在 Kotlin 上面,我们无需进行重载,可以直接在方法上面直接定义参数的默认值
1 | fun toast(context : Context = this, text : String, time : Int = Toast.LENGTH_SHORT) { |
6、类方法扩展
可以在不用继承的情况下对扩展原有类的方法,例如对 String 类进行扩展方法
1 | fun String.handle() : String { |
7、运算重载符
在 Kotlin 中使用运算符最终也会调用对象对应的方法,我们可以通过重写这些方法使得这个对象支持运算符,这里不再演示代码
运算符 | 调用方法 |
---|---|
+a | a.unaryPlus() |
-a | a.unaryMinus() |
!a | a.not() |
运算符 | 调用方法 |
---|---|
a++ | a.inc() |
a– | a.dec() |
运算符 | 调用方法 |
---|---|
a + b | a.plus(b) |
a - b | a.minus(b) |
a * b | a.times(b) |
a / b | a.div(b) |
a % b | a.rem(b), a.mod(b) (deprecated) |
a..b | a.rangeTo(b) |
运算符 | 调用方法 |
---|---|
a in b | b.contains(a) |
a !in b | !b.contains(a) |
运算符 | 调用方法 |
---|---|
a[i] | a.get(i) |
a[i, j] | a.get(i, j) |
a[i_1, …, i_n] | a.get(i_1, …, i_n) |
a[i] = b | a.set(i, b) |
a[i, j] = b | a.set(i, j, b) |
a[i_1, …, i_n] = b | a.set(i_1, …, i_n, b) |
运算符 | 调用方法 |
---|---|
a() | a.invoke() |
a(i) | a.invoke(i) |
a(i, j) | a.invoke(i, j) |
a(i_1, …, i_n) | a.invoke(i_1, …, i_n) |
运算符 | 调用方法 |
---|---|
a += b | a.plusAssign(b) |
a -= b | a.minusAssign(b) |
a *= b | a.timesAssign(b) |
a /= b | a.divAssign(b) |
a %= b | a.remAssign(b), a.modAssign(b) (deprecated) |
运算符 | 调用方法 |
---|---|
a == b | a?.equals(b) ?: (b === null) |
a != b | !(a?.equals(b) ?: (b === null)) |
运算符 | 调用方法 |
---|---|
a > b | a.compareTo(b) > 0 |
a < b | a.compareTo(b) < 0 |
a >= b | a.compareTo(b) >= 0 |
a <= b | a.compareTo(b) <= 0 |
8、扩展函数
扩展函数是 kotlin 用于简化一些代码的书写产生的,其中有 let、with、run、apply、also 五个函数。
let 函数
在函数块内可以通过 it 指代该对象。返回值为函数块的最后一行或指定return表达式。
一般写法
1 | fun main() { |
let 写法
1 | fun main() { |
最常用的场景就是使用let函数处理需要针对一个可null的对象统一做判空处理
1 | mVideoPlayer?.setVideoView(activity.course_video_view) |
又或者是需要去明确一个变量所处特定的作用域范围内可以使用。
with 函数
前面的几个函数使用方式略有不同,因为它不是以扩展的形式存在的。它是将某对象作为函数的参数,在函数块内可以通过 this 指代该对象。返回值为函数块的最后一行或指定return表达式
定义 Person 类
1 | class Person(var name : String, var age : Int) |
一般写法
1 | fun main() { |
with 写法
1 | fun main() { |
适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可,经常用于Android中RecyclerView中onBinderViewHolder中,数据model的属性映射到UI上
1 | override fun onBindViewHolder(holder: ViewHolder, position: Int){ |
1 | override fun onBindViewHolder(holder: ViewHolder, position: Int){ |
run 函数
实际上可以说是let和with两个函数的结合体,run函数只接收一个lambda函数为参数,以闭包形式返回,返回值为最后一行的值或者指定的return的表达式
一般写法
1 | var person = Person("Android轮子哥", 100) |
run 写法
1 | var person = Person("Android轮子哥", 100) |
适用于let,with函数任何场景。因为run函数是let,with两个函数结合体,准确来说它弥补了let函数在函数体内必须使用it参数替代对象,在run函数中可以像with函数一样可以省略,直接访问实例的公有属性和方法,另一方面它弥补了with函数传入对象判空问题,在run函数中可以像let函数一样做判空处理,这里还是借助 onBindViewHolder 案例进行简化。
1 | override fun onBindViewHolder(holder: ViewHolder, position: Int){ |
apply 函数
从结构上来看apply函数和run函数很像,唯一不同点就是它们各自返回的值不一样,run函数是以闭包形式返回最后一行代码的值,而apply函数的返回的是传入对象的本身
一般写法
1 | val person = Person("Android轮子哥", 100) |
apply 写法
1 | val person = Person("Android轮子哥", 100).apply { |
整体作用功能和run函数很像,唯一不同点就是它返回的值是对象本身,而run函数是一个闭包形式返回,返回的是最后一行的值。正是基于这一点差异它的适用场景稍微与run函数有点不一样。apply一般用于一个对象实例初始化的时候,需要对对象中的属性进行赋值。
或者动态inflate出一个XML的View的时候需要给View绑定数据也会用到,这种情景非常常见。特别是在我们开发中会有一些数据model向View model转化实例化的过程中需要用到。
1 | mRootView = View.inflate(activity, R.layout.example_view, null) |
使用 apply 函数后的代码是这样的
1 | mRootView = View.inflate(activity, R.layout.example_view, null).apply { |
多层级判空问题
1 | if (mSectionMetaData == null || mSectionMetaData.questionnaire == null || mSectionMetaData.section == null) { |
kotlin的apply函数优化
1 | mSectionMetaData?.apply { |
also 函数
also函数的结构实际上和let很像唯一的区别就是返回值的不一样,let是以闭包的形式返回,返回函数体内最后一行的值,如果最后一行为空就返回一个Unit类型的默认值。而also函数返回的则是传入对象的本身
1 | fun main() { |
适用于let函数的任何场景,also函数和let很像,只是唯一的不同点就是let函数最后的返回值是最后一行的返回值而also函数的返回值是返回当前的这个对象。一般可用于多个扩展函数链式调用
总结
通过以上几种函数的介绍,可以很方便优化kotlin中代码编写,整体看起来几个函数的作用很相似,但是各自又存在着不同。使用的场景有相同的地方比如run函数就是let和with的结合体
9、协程
子任务协作运行,优雅的处理异步问题解决方案。
协程实际上就是极大程度的复用线程,通过让线程满载运行,达到最大程度的利用CPU,进而提升应用性能。
https://www.kotlincn.net/docs/reference/coroutines/coroutines-guide.html