如果对 Kotlin 语法不熟悉的,推荐看这篇文章:全民Kotlin:Java我们不一样

无需 findViewById

在布局中定义

android:id="@+id/tv_content"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="Hello World!" />

直接设置 TextView 的文本

tv_content.text = "改变文本"

Lambda

Lambda 表达式虽然在 Java JDK 已经加上了,但是没有普及开来,现在搭配 Kotlin 是一个不错的选择

tv_content.setOnClickListener(View.OnClickListener(

fun(v : View) {

v.visibility = View.GONE

}

))

现在可以用 Lambda 表达式进行简化

tv_content.setOnClickListener { v -> v.visibility = View.GONE }

函数变量

在 Kotlin 语法中函数是可以作为变量进行传递的

var result = fun(number1 : Int, number2 : Int) : Int {

return number1 + number2

}

使用这个函数变量

println(result(1, 2))

空安全

在 Java 不用强制我们处理空对象,所以常常会导致 NullPointerException 空指针出现,现在 Kotlin 对空对象进行了限定,必须在编译时处理对象是否为空的情况,不然会编译不通过

在对象不可空的情况下,可以直接使用这个对象

fun getText() : String {

return "text"

}

val text = getText()

print(text.length)

在对象可空的情况下,必须要判断对象是否为空

fun getText() : String? {

return null

}

val text = getText()

if (text != null) {

print(text.length)

}

// 如果不想判断是否为空,可以直接这样,如果 text 对象为空,则会报空指针异常,一般情况下不推荐这样使用

val text = getText()

print(text!!.length)

// 还有一种更好的处理方式,如果 text 对象为空则不会报错,但是 text.length 的结果会等于 null

val text = getText()

print(text?.length)

方法支持添加默认参数

在 Java 上,我们可能会为了扩展某个方法而进行多次重载

public void toast(String text) {

toast(this, text, Toast.LENGTH_SHORT);

}

public void toast(Context context, String text) {

toast(context, text, Toast.LENGTH_SHORT);

}

public void toast(Context context, String text, int time) {

Toast.makeText(context, text, time).show();

}

toast("弹个吐司");

toast(this, "弹个吐司");

toast(this, "弹个吐司", Toast.LENGTH_LONG);

但是在 Kotlin 上面,我们无需进行重载,可以直接在方法上面直接定义参数的默认值

fun toast(context : Context = this, text : String, time : Int = Toast.LENGTH_SHORT) {

Toast.makeText(context, text, time).show()

}

toast(text = "弹个吐司")

toast(this, "弹个吐司")

toast(this, "弹个吐司", Toast.LENGTH_LONG)

类方法扩展

可以在不用继承的情况下对扩展原有类的方法,例如对 String 类进行扩展方法

fun String.handle() : String {

return this + "Android轮子哥"

}

// 需要注意,handle 方法在哪个类中被定义,这种扩展只能在那个类里面才能使用

print("HJQ = ".handle())

HJQ = Android轮子哥

运算符重载

在 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

扩展函数

扩展函数是 Kotlin 用于简化一些代码的书写产生的,其中有 let、with、run、apply、also 五个函数

let 函数

在函数块内可以通过 it 指代该对象。返回值为函数块的最后一行或指定return表达式

一般写法

fun main() {

val text = "Android轮子哥"

println(text.length)

val result = 1000

println(result)

}

let 写法

fun main() {

val result = "Android轮子哥".let {

println(it.length)

1000

}

println(result)

}

最常用的场景就是使用let函数处理需要针对一个可null的对象统一做判空处理

mVideoPlayer?.setVideoView(activity.course_video_view)

mVideoPlayer?.setControllerView(activity.course_video_controller_view)

mVideoPlayer?.setCurtainView(activity.course_video_curtain_view)

mVideoPlayer?.let {

it.setVideoView(activity.course_video_view)

it.setControllerView(activity.course_video_controller_view)

it.setCurtainView(activity.course_video_curtain_view)

}

又或者是需要去明确一个变量所处特定的作用域范围内可以使用

with 函数

前面的几个函数使用方式略有不同,因为它不是以扩展的形式存在的。它是将某对象作为函数的参数,在函数块内可以通过 this 指代该对象。返回值为函数块的最后一行或指定return表达式

定义 Person 类

class Person(var name : String, var age : Int)

一般写法

fun main() {

var person = Person("Android轮子哥", 100)

println(person.name + person.age)

var result = 1000

println(result)

}

with 写法

fun main() {

var result = with(Person("Android轮子哥", 100)) {

println(name + age)

1000

}

println(result)

}

适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可,经常用于Android中RecyclerView中onBinderViewHolder中,数据model的属性映射到UI上

override fun onBindViewHolder(holder: ViewHolder, position: Int){

val item = getItem(position)?: return

holder.nameView.text = "姓名:${item.name}"

holder.ageView.text = "年龄:${item.age}"

}

override fun onBindViewHolder(holder: ViewHolder, position: Int){

val item = getItem(position)?: return

with(item){

holder.nameView.text = "姓名:$name"

holder.ageView.text = "年龄:$age"

}

}

run 函数

实际上可以说是let和with两个函数的结合体,run函数只接收一个lambda函数为参数,以闭包形式返回,返回值为最后一行的值或者指定的return的表达式

一般写法

var person = Person("Android轮子哥", 100)

println(person.name + "+" + person.age)

var result = 1000

println(result)

run 写法

var person = Person("Android轮子哥", 100)

var result = person.run {

println("$name + $age")

1000

}

println(result)

适用于let,with函数任何场景。因为run函数是let,with两个函数结合体,准确来说它弥补了let函数在函数体内必须使用it参数替代对象,在run函数中可以像with函数一样可以省略,直接访问实例的公有属性和方法,另一方面它弥补了with函数传入对象判空问题,在run函数中可以像let函数一样做判空处理,这里还是借助 onBindViewHolder 案例进行简化

override fun onBindViewHolder(holder: ViewHolder, position: Int){

val item = getItem(position)?: return

holder.nameView.text = "姓名:${item.name}"

holder.ageView.text = "年龄:${item.age}"

}

override fun onBindViewHolder(holder: ViewHolder, position: Int){

val item = getItem(position)?: return

item?.run {

holder.nameView.text = "姓名:$name"

holder.ageView.text = "年龄:$age"

}

}

apply 函数

从结构上来看apply函数和run函数很像,唯一不同点就是它们各自返回的值不一样,run函数是以闭包形式返回最后一行代码的值,而apply函数的返回的是传入对象的本身

一般写法

val person = Person("Android轮子哥", 100)

person.name = "HJQ"

person.age = 50

apply 写法

val person = Person("Android轮子哥", 100).apply {

name = "HJQ"

age = 50

}

整体作用功能和run函数很像,唯一不同点就是它返回的值是对象本身,而run函数是一个闭包形式返回,返回的是最后一行的值。正是基于这一点差异它的适用场景稍微与run函数有点不一样。apply一般用于一个对象实例初始化的时候,需要对对象中的属性进行赋值。或者动态inflate出一个XML的View的时候需要给View绑定数据也会用到,这种情景非常常见。特别是在我们开发中会有一些数据model向View model转化实例化的过程中需要用到

mRootView = View.inflate(activity, R.layout.example_view, null)

mRootView.tv_cancel.paint.isFakeBoldText = true

mRootView.tv_confirm.paint.isFakeBoldText = true

mRootView.seek_bar.max = 10

mRootView.seek_bar.progress = 0

使用 apply 函数后的代码是这样的

mRootView = View.inflate(activity, R.layout.example_view, null).apply {

tv_cancel.paint.isFakeBoldText = true

tv_confirm.paint.isFakeBoldText = true

seek_bar.max = 10

seek_bar.progress = 0

}

多层级判空问题

if (mSectionMetaData == null || mSectionMetaData.questionnaire == null || mSectionMetaData.section == null) {

return;

}

if (mSectionMetaData.questionnaire.userProject != null) {

renderAnalysis();

return;

}

if (mSectionMetaData.section != null && !mSectionMetaData.section.sectionArticles.isEmpty()) {

fetchQuestionData();

return;

}

kotlin的apply函数优化

mSectionMetaData?.apply {

//mSectionMetaData不为空的时候操作mSectionMetaData

}?.questionnaire?.apply {

//questionnaire不为空的时候操作questionnaire

}?.section?.apply {

//section不为空的时候操作section

}?.sectionArticle?.apply {

//sectionArticle不为空的时候操作sectionArticle

}

also 函数

also函数的结构实际上和let很像唯一的区别就是返回值的不一样,let是以闭包的形式返回,返回函数体内最后一行的值,如果最后一行为空就返回一个Unit类型的默认值。而also函数返回的则是传入对象的本身

fun main() {

val result = "Android轮子哥".let {

println(it.length)

1000

}

println(result) // 打印:1000

}

fun main() {

val result = "Android轮子哥".also {

println(it.length)

}

println(result) // 打印:Android轮子哥

}

适用于let函数的任何场景,also函数和let很像,只是唯一的不同点就是let函数最后的返回值是最后一行的返回值而also函数的返回值是返回当前的这个对象。一般可用于多个扩展函数链式调用

总结

通过以上几种函数的介绍,可以很方便优化kotlin中代码编写,整体看起来几个函数的作用很相似,但是各自又存在着不同。使用的场景有相同的地方比如run函数就是let和with的结合体

协程

子任务协作运行,优雅的处理异步问题解决方案

协程实际上就是极大程度的复用线程,通过让线程满载运行,达到最大程度的利用CPU,进而提升应用性能

在当前 app module 中配置环境和依赖(因为现在协程在 Kotlin 中是实验性的)

kotlin {

experimental {

coroutines 'enable'

}

}

dependencies {

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.20'

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:0.20'

}

协程的三种启动方式

runBlocking:T

launch:Job

async/await:Deferred

runBlocking

runBlocking 的中文翻译:运行阻塞。说太多没用,直接用代码测试一下

println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))

println("测试开始")

runBlocking {

println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))

println("测试延迟开始")

delay(20000) // 因为 Activity 最长响应时间为 15 秒

println("测试延迟结束")

}

println("测试结束")

17:02:08.686 System.out: 测试是否为主线程true

17:02:08.686 System.out: 测试开始

17:02:08.688 System.out: 测试是否为主线程true

17:02:08.688 System.out: 测试延迟开始

17:02:28.692 System.out: 测试延迟结束

17:02:28.693 System.out: 测试结束

runBlocking 运行在主线程,过程中 App 出现过无响应提示,由此可见 runBlocking 和它的名称一样,真的会阻塞当前的线程,只有等 runBlocking 里面的代码执行完了才会执行 runBlocking 外面的代码

launch

launch 的中文翻译:启动。甭管这是啥,直接用代码测试

println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))

println("测试开始")

launch {

println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))

println("测试延迟开始")

delay(20000)

println("测试延迟结束")

}

println("测试结束")

17:19:17.190 System.out: 测试是否为主线程true

17:19:17.190 System.out: 测试开始

17:19:17.202 System.out: 测试结束

17:19:17.203 System.out: 测试是否为主线程false

17:19:17.203 System.out: 测试延迟开始

17:19:37.223 System.out: 测试延迟结束

async

async 的中文翻译:异步。还是老套路,直接上代码

测试的时候是主线程,但是到了 launch 中就会变成子线程,这种效果类似 new Thread(),有木有?和 runBlocking 最不同的是 launch 没有执行顺序这个概念

println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))

println("测试开始")

async {

println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))

println("测试延迟开始")

delay(20000)

println("测试延迟结束")

}

println("测试结束")

17:29:00.694 System.out: 测试是否为主线程true

17:29:00.694 System.out: 测试开始

17:29:00.697 System.out: 测试结束

17:29:00.697 System.out: 测试是否为主线程false

17:29:00.697 System.out: 测试延迟开始

17:29:20.707 System.out: 测试延迟结束

这结果不是跟 launch 一样么?那么这两个到底有什么区别呢?,让我们先看一段测试代码

println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))

println("测试开始")

val async = async {

println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))

println("测试延迟开始")

delay(20000)

println("测试延迟结束")

return@async "666666"

}

println("测试结束")

runBlocking {

println("测试返回值:" + async.await())

}

17:50:57.117 System.out: 测试是否为主线程true

17:50:57.117 System.out: 测试开始

17:50:57.120 System.out: 测试结束

17:50:57.120 System.out: 测试是否为主线程false

17:50:57.120 System.out: 测试延迟开始

17:51:17.131 System.out: 测试延迟结束

17:51:17.133 System.out: 测试返回值:666666

看到这里你是否懂了,async 和 launch 还是有区别的,async 可以有返回值,通过它的 await 方法进行获取,需要注意的是这个方法只能在协程的操作符中才能调用

线程调度

啥?协程有类似 RxJava 线程调度?先用 launch 试验一下

println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))

println("测试开始")

launch(CommonPool) { // 同学们,敲重点

println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))

println("测试延迟开始")

delay(20000)

println("测试延迟结束")

}

println("测试结束")

18:00:23.243 System.out: 测试是否为主线程true

18:00:23.244 System.out: 测试开始

18:00:23.246 System.out: 测试结束

18:00:23.246 System.out: 测试是否为主线程false

18:00:23.247 System.out: 测试延迟开始

18:00:43.256 System.out: 测试延迟结束

你:你怕不是在逗我?这个跟刚刚的代码有什么不一样吗?

我:当然不一样,假如一个网络请求框架维护了一个线程池,一个图片加载框架也维护了一个线程池.......,你会发现其实这样不好的地方在于,这些线程池里面的线程没有被重复利用,于是乎协程主动维护了一个公共的线程池 CommonPool,很好的解决了这个问题

你:你说得很有道理,还有刚刚不是说能线程调度吗?为什么还是在子线程运行?

我:因为我刚刚只用了 CommonPool 这个关键字,我再介绍另一个关键字 UI,光听名字就知道是啥了

println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))

println("测试开始")

launch(UI) {

println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))

println("测试延迟开始")

delay(20000)

println("测试延迟结束")

}

println("测试结束")

18:07:20.181 System.out: 测试是否为主线程true

18:07:20.181 System.out: 测试开始

18:07:20.186 System.out: 测试结束

18:07:20.192 System.out: 测试是否为主线程true

18:07:20.192 System.out: 测试延迟开始

18:07:40.214 System.out: 测试延迟结束

Android开发进阶Q群:78797078

全民java竞争有多激烈,全民Kotlin:你没有玩过的全新玩法相关推荐

  1. 全民 Kotlin:你没有玩过的全新玩法

    空安全 在 Java 不用强制我们处理空对象,所以常常会导致 NullPointerException 空指针出现,现在 Kotlin 对空对象进行了限定,必须在编译时处理对象是否为空的情况,不然会导 ...

  2. 为什么 Java 后端开发没有大规模采用 Kotlin?

    点击关注公众号,Java干货及时送达 转自:InfoQ 作者:Ivan Sanchez,  译者:王者 链接:www.infoq.cn/article/dSSYAFSMMxx1sZkJwrN2 在使用 ...

  3. 从 Java 到 Scala,再到 Kotlin

    在 Java 之后,JVM 平台上出现了一些其他的编程语言,Scala 和 Kotlin 可以算是其中的佼佼者.Scala 已成为大数据领域的明星,Kotlin 在 2017 年 Google IO ...

  4. 2018计算机专业高考人数,2018高考报名人数创八年来新高,高考竞争会越来越激烈吗?...

    全国高考报名人数在经历四年稳定之后,2018年迎来大涨,报名人数达到975万,较2017年增加35万,创近8年新高. 据中国教育在线日前发布的<2018年高招调查报告>显示,高考报名人数迎 ...

  5. 西北电力外送争夺战 竞争将更加激烈

    外送,一直是西北几个省份电力消纳的主要方式.不过,对于普遍面临"弃光"的西北省份而言,目前各自面临的局面已出现了些许的分化. 数据显示,借助"电力援疆",今年新 ...

  6. 空间大战java_回忆S60(塞班)年代的JAVA游戏:有没有哪一款是你在课堂偷偷玩的?...

    原标题:回忆S60(塞班)年代的JAVA游戏:有没有哪一款是你在课堂偷偷玩的? 那时候的手机内存很小,要用来玩游戏的话就必须买一个大内存的MMC卡,手机也没那么多款式,班上同学几乎用着同样的几款手机, ...

  7. 我的世界java版使用剑_我的世界:JAVA版藏“私货”内置绝世好剑与神功,你玩的版本有吗...

    导语:我的世界:JAVA版藏"私货"内置绝世好剑与神功,你玩的版本有吗! 在我的世界这款游戏中,每件物品都有它自己存在的作用,铁镐挖矿,盔甲防护,却也有一些物品被创作者赋予了奇怪的 ...

  8. 抖抖口红机java版本_抖音全民口红机

    口红机游戏app是一款可以抓口红的娃娃机,简单有趣的玩法,玩家完成闯关即可获的想要的口红,生活娱乐相互结合,各类大牌口红随你领取,全新的抓娃娃体验,爱生活就来东坡下载体验吧! 口红机软件简介: 全民口 ...

  9. java飞机大战护盾_全民飞机大战无敌护盾使用方法分享

    全民飞机大战中有很多无敌护盾,无敌护盾大家都知道,我敢说有的人不会用无敌护盾,这里肯定有人嘲笑我,谁不会用,那么简单,也就你不会吧,其实我以前是不会的,最近才学会怎么正确的使用无敌护盾,现在小编我给大 ...

最新文章

  1. GRE写作-学生选择有助于就业的课程or根据兴趣选课?
  2. Collaborative Filter - Data Mining基础(ACM暑校)
  3. LeetCode:Add Two Numbers
  4. phpstorm-有关设置
  5. dockerfile mysql例子_docker-compose 实用示例
  6. C#中拷贝指定文件夹下的所有文件夹目录到指定文件夹中的方法
  7. Java 8 时间日期库的20个使用演示样例
  8. vscode中前端vue项目详解_web前端Vue项目实战-Music
  9. Team Foundation 使用第三方比较工具
  10. 基于FTP4J组件的FTP操作客户端
  11. matlab quiver 箭头颜色,matlab – quiver3箭头颜色对应大小
  12. html页面乱码解决
  13. exoplayer和mediaplayer的封装
  14. 大学生怎样学习一门编程
  15. word去掉标题前面的黑点
  16. 【git】git的删除命令与如何删除仓库文件的方法
  17. 第一次学游泳技巧_初学者第一次下水需要注意哪些情况,新手学习游泳需要掌握什么技巧,适合的游泳装备有哪些...
  18. 【渝粤题库】陕西师范大学203041 行政法与行政诉讼法学作业
  19. 对8086的存储器扩展
  20. border之border-style用法

热门文章

  1. 微信小程序登录流程php,微信小程序登录流程​
  2. Java笔记(P400/241-P427/269)
  3. jvm优化—监控工具:诊断内存泄露、cpu飙升、线程死锁、响应变慢
  4. java mysql 变量_mysql中的变量
  5. 供应链对企业竞争有哪些优势?
  6. 武汉专科学校计算机专业分数线,湖北省专科大学分数线(有哪些)
  7. 【SSL 1502】校门外的树【树状数组】
  8. 给出一个 netdump 程序, 抓包用的. 如果改进了,也希望贴出(摘自Chinaunix-论坛首页-C/C++)
  9. 微信小程序:长按图片识别二维码
  10. 谈一谈|MkDocs介绍及应用