Android必备技能之一:Kotlin(一)
1、Kotlin前世与今生
- 写了太久Java,有没有发现其实你写了太多冗余的代码?
- 你虽然小心翼翼,可总是被QA折腾出来的NullPointerException所头疼,难道就没有受够这种日子么?
- 直到有一天你发现自己代码除了if,else,for循环,竟然没有任何留恋?
那么我们可以一起来尝试一下 Kotlin,话说回来了什么是Kotlin呢?
Kotlin是基于JVM新的编程语言,由 JetBrains 开发,可以编译成java字节码,也可以编译成JavaScript。而JetBrains,作为目前广受欢迎的Java IDE IntelliJ 的提供商,也在 Apache 许可下已经开源其Kotlin 编程语言。
2、Kotlin环境配置
接下来的我就直接在Android Studio(下面简称AS)环境上面操作,虽然使用IDE IntelliJ也可以实现,但AS上也是对Kotlin支持的,首先下载以下相关插件(虽然并不是所有插件一次性用到,但建议一次性下载完,后续就不需要下载了):
- Kotlin
- Kotlin Extensions For Android
- Anko DSL Preview
其中Anko DSL Preview插件用于预览使用DSL编写的UI代码,就像以前使用xml编写UI文件时可以动态在“Preview”窗口预览效果一样。
上面三个插件下载安装重启之后,然后新建一个项目默认配置Gradle如下:
ext.kotlin_version = '1.1.0'
ext.anko_version = '0.8.2'
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
.........................
dependencies {
.........................
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
compile "org.jetbrains.anko:anko-sdk15:$anko_version"
compile "org.jetbrains.anko:anko-support-v4:$anko_version"
.........................
}
repositories {
mavenCentral()
}
这里添加了Kotlin对android的扩展,同时也添加了Kotlin的gradle插件。我们打开系统默认帮我们建的MainActivity,然后Code->Convert Java File to Kotlin File->OK,此时我们原有的Activity应该将我们转换成了如下内容:
class KoTlinActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_ko_tlin)
}
}
上面这一步是将java代码转换为Kotlin代码。截止到现在,你什么都不用做,程序就已经可以跑起来了。你可能要说代码相比之下并没有简洁多少啊 ,当然到这里并没有结束,反而是开始,只是首先一起来体验下我们的第一个Kotlin代码程序。
3、Kotlin完美给Java开发者打造
3.1 通用的集合框架和Kotlin的扩展
通用的集合框架,ex如下:
val list = arrayListOf(1, 2, 3, 4)
list.add(5)
list.remove(3)
for (item in list) {
debug(item);
}
效果如下:
03-09 16:06:23.991 21108-21108/com.Igeek.kotlin D/MainActivity: 1
03-09 16:06:23.991 21108-21108/com.Igeek.kotlin D/MainActivity: 2
03-09 16:06:23.991 21108-21108/com.Igeek.kotlin D/MainActivity: 4
03-09 16:06:23.991 21108-21108/com.Igeek.kotlin D/MainActivity: 5
可以看到这里Kotlin写法和java是差不多的。
至于Kotlin的扩展,其实就是对java的库进行了进一步扩展,ex如下:
val list = arrayListOf(1, 2, 3, 4, 5)
list.forEach {
debug(it)
}
debug("==========================")
val doubleList = list.map {
it * 2
}
doubleList.forEach {
debug(it)
}
debug("==========================")
val oddList = list.filter{
it % 2 == 1
}
oddList.forEach {
debug(it)
}
打印如下:
03-09 16:18:06.978 28828-28828/com.Igeek.kotlin D/MainActivity: 1
03-09 16:18:06.979 28828-28828/com.Igeek.kotlin D/MainActivity: 2
03-09 16:18:06.979 28828-28828/com.Igeek.kotlin D/MainActivity: 3
03-09 16:18:06.979 28828-28828/com.Igeek.kotlin D/MainActivity: 4
03-09 16:18:06.979 28828-28828/com.Igeek.kotlin D/MainActivity: 5
03-09 16:18:06.979 28828-28828/com.Igeek.kotlin D/MainActivity: ==========================
03-09 16:18:06.979 28828-28828/com.Igeek.kotlin D/MainActivity: 2
03-09 16:18:06.979 28828-28828/com.Igeek.kotlin D/MainActivity: 4
03-09 16:18:06.979 28828-28828/com.Igeek.kotlin D/MainActivity: 6
03-09 16:18:06.979 28828-28828/com.Igeek.kotlin D/MainActivity: 8
03-09 16:18:06.979 28828-28828/com.Igeek.kotlin D/MainActivity: 10
03-09 16:18:06.979 28828-28828/com.Igeek.kotlin D/MainActivity: ==========================
03-09 16:18:06.979 28828-28828/com.Igeek.kotlin D/MainActivity: 1
03-09 16:18:06.979 28828-28828/com.Igeek.kotlin D/MainActivity: 3
03-09 16:18:06.979 28828-28828/com.Igeek.kotlin D/MainActivity: 5
3.1 Kotlin与Java的交互
Kotlin的标准库更多的是对Java库的扩展,基于这个设计思路,你丝毫不需要担心Kotlin对Java代码的引用,你甚至可以在Kotlin当中使用Java反射,反正只要是Java有的,Kotlin都有。最常见的就是Getter/Setter方法对应到Kotlin属性的调用,ex如下:
首先准备一个java类:
调用如下:
val javaPerson = Person()
javaPerson.address = "Wo shi Kotlin"
debug(javaPerson.address)
打印结果就是"Wo shi Kotlin"了,刚刚只顾着写,忘了提一句,在Kotlin语法里面,是不用在后面加“;”的,有没有感觉很爽,废话不说了,可以看到我们调用时候,既没有用到get方法也没有用到set方法,是不是避免了总会有人说get/set方法影响性能的问题。
官网地址(有详细的比较):https://kotlinlang.org/docs/reference/java-interop.html#static-methods-and-fields
4、简洁 可靠 有趣
4.1数据类
在JavaBean中我们往往会覆写诸如equals和hashcode等方法,一旦用到HashMap这样的集合框架,总是出了问题都不知道找谁。Kotlin提供了一种非常简单的方式来创建这样的数据类,ex如下:
data class Main(val id:Int,val name:String)
//main方法调用
fun main(args:Array<out String>){
println(Main(0,"Main函数"))
}
打印结果:
Main(id=0, name=Main函数)
仅仅一行代码,Kotlin就会创建出一个完整的数据类,并自动生成相应的equals、hashcode、toString方法。
4.2 空安全与属性代理
想想平时QA提的bug,不太靠谱的server,不太确定数据类型,均可能出现Exception,但我们总不能在所有地方都进行判断,第一次看到Kotlin的空安全处理,真的眼前一亮。
Kotlin的空安全设计,主要是在类型后面加?表示可空,否则就不能为null,ex如下:
val nullable: Int? = 0
val nonNullable: Int = 2
nullable.toFloat() // 编译错误
nullable?.toFloat() // 如果null,什么都不做,否则调用toFloat
nullable!!.toFloat() // 强制转换为非空对象,并调用toFloat;如果nullable为null,抛空指针异常
nonNullable.toFloat() // 正确
我们利用Convert Java File to Kotlin File生成的Kotlin代码,在onCreate方法中也是如此考虑,savedInstanceState是否为空。
override fun onCreate(savedInstanceState: Bundle?) {}
注:这里的空指针异常是KotlinNullPointerException,而不是Java的NullPointerException。
5、Kotlin场景使用
好了,接下来我们就实战说说Kotlin的用法,用如下代码举例:
java代码:
Kotlin代码:
46EA6401-2A70-40FA-91A5-9EAA38E76670.png5.1 场景一(控件findViewById)
findViewById有很多写法,我们就从复杂到容易说起:
写法一:
private var tv_hello_view: TextView? = null
.......................................
tv_hello_view = findViewById(R.id.tv_hello_view) as TextView
tv_hello_view!!.text = "Say Hello!!!"
tv_hello_view!!.textSize = 22f
tv_hello_view!!.setOnClickListener {}
有没有发现写法一还不如java写法,貌似java写法还简单一点,并且设置text和size时候,需要加两个叹号,不加的话貌似编译器并不识别你是否为null,下面我们来看写法二:
写法二:
private val tv_hello_view: TextView by lazy{
findViewById(R.id.tv_hello_view) as TextView
}
.......................................
tv_hello_view.text = "Say Hello!!!"
tv_hello_view.textSize = 22f
tv_hello_view.setOnClickListener {}
这种写法好像简单了一点,不过好像依然很复杂,注意在这里初始化的时候不要直接
private var tv_hello_view: TextView //编译错误
lazy是Kotlin的属性代理的一个实例,它提供了延迟加载的机制。换句话说,这里的lazy提供了初始化aTextView的方法,不过真正初始化这个动作发生的时机却是在aTextView第一次被使用时了。lazy默认是线程安全的,你当然也可以关掉这个配置,只需要加个参数LazyThreadSafetyMode.NONE即可:
private val tv_hello_view: TextView by lazy(LazyThreadSafetyMode.NONE){
findViewById(R.id.tv_hello_view) as TextView
}
写法三:
private lateinit var tv_hello_view: TextView
.......................................
tv_hello_view = findViewById(R.id.tv_hello_view) as TextView
tv_hello_view.text = "Say Hello!!!"
tv_hello_view.textSize = 22f
tv_hello_view.setOnClickListener {}
这里主要用了lateinit 来修饰它,方法简单了不少吧 ,但是findViewById这个单词好长啊,能不能简化啊,答案是肯定的,我们请出Anko,注意我们有依赖过dependencies哟。
写法四:
private lateinit var tv_hello_view: TextView
.......................................
tv_hello_view = find(R.id.tv_hello_view)
tv_hello_view.text = "Say Hello!!!"
tv_hello_view.textSize = 22f
tv_hello_view.setOnClickListener {}
可以看到我们方法改成了find,并且没了 as TextView,注意我们需要import org.jetbrains.anko.find。既然请出来了Anko ,那么我们还有终极方案,完全去除findViewById。
写法五:
CD9648CF-79D0-48FB-AED7-59A2CAAF448B.png可以发现直接操作tv_hello_view,这便是findViewById的终极写法。
注:
1.导入了import kotlinx.android.synthetic.main.hello_layout.*包
2. tv_hello_view是hello_layout布局xml中的id
写到这里,内容也挺多了,我也只是把用法统一归纳一下,至于findViewById所牵扯出来的几个点:lazy,primitives, lateinit, Anko以及不要 findViewById仍然能找到控件的原理我并没有去细致分析,后续有时间我会再补充上,当然如果想更多的去了解,可以有一下资料参考:
Kotlin官网文档地址:https://kotlinlang.org/docs/
《Kotlin for android Developers》中文翻译: https://github.com/wangjiegulu/kotlin-for-android-developers-zh/blob/master/SUMMARY.md
在这里推荐一部Kotlin基础学习视频:https://pan.baidu.com/s/1b2tBH0 提取码:nryk
本博客参考了一下文献:
Kotlin for android Developers》中文翻译;
博客:http://www.println.net/post/Android-A-Powerful-Substitution-Kotlin;
博客:http://mp.weixin.qq.com/s?__biz=MzIzMTYzOTYzNA==&mid=100000121&idx=1&sn=6a8c4b27dec4e03a58e888c5fa18b7e2&chksm=68a05e445fd7d752da50717bec037f51702aa9557b308114f2d7255109509bcd24c1a5d80903&mpshare=1&scene=23&srcid=0309PegdxPNifENVckrvhBZY#rd