1.1 Kotlin的身世
-
写了许久 Java,有没有发现其实你写了太多冗余的代码?
-
后来你体验了一下 Python,有没有觉得不写分号的感觉真是超级爽?
-
你虽然勤勤恳恳,可到头来却被 NullPointerException 折磨的死去活来,难道就没有受够这种日子么?
-
直到有一天你发现自己已经写了好几十万行代码,发现居然全是 getter 和 setter!
哈哈,实际上你完全可以不用这么痛苦,用 Kotlin 替代 Java 开发你的程序,无论是 Android 还是 Server,你都能像之前写 Java 一样思考,同时又能享受到新一代编程语言的特性,说到这里你是不是开始心动了呢?下面我就通过这篇文章来给大家介绍一下 Kotlin 究竟是何方神圣。
话说,Kotlin 是 JetBrain 公司搞出来的,运行在 JVM 上的一门静态类型语言,它是用波罗的海的一个小岛的名字命名的。从外观上,乍一看还以为是 Scala,我曾经琢磨着把 Scala 作为我的下一门语言,不过想想用 Scala 来干嘛呢,我又不做大数据,而它又太复杂了o(╯□╰)o
用Kotlin创建一个数据类
最初是在 intelliJ 的源码中看到 Kotlin 的,那时候 Kotlin 的版本还不太稳定,所以源码总是编译不过,真是要抓狂啊,还骂『什么破玩意儿!为什么又出来新语言了?Groovy 还没怎么学会,又来个 Kotlin!』话说,Kotlin,难道是『靠它灵』的意思??
其实经过一年多的发展,Kotlin 1.0已经 release,feature 基本完善,api 也趋于稳定,这时候尝试也不会有那种被坑的感觉了。过年期间也算清闲,于是用 Kotlin 做了个 app,简单来说,就是几个感觉:
-
思路与写 Java 时一样,不过更简洁清爽
-
少了冗余代码的烦恼,更容易专注于功能的开发,整个过程轻松愉快
-
扩展功能使得代码写起来更有趣
-
空安全和不可变类型使得开发中对变量的定义和初始化倾注了更多关注
-
啊啊,我再也不用写那个 findViewById 了,真的爽爆有木有!
1.2 第一个Kotlin程序
Kotlin 开发当然使用 JetBrain 系列的 IDE,实际上 intelliJ idea 15 发布时就已经内置了 Kotlin 插件,更早的版本则需要到插件仓库中下载安装 Kotlin 插件——在安装时你还会看到有个 Kotlin Extensions for Android,不要管他,已经过时了。安装好以后,我们就可以使用 Kotlin 进行开发了。
接下来我们用 Android Studio 创建一个 Android 工程,比如叫做 HelloKotlin,在 app 目录下面的 build.gradle 文件中添加下面的配置:
这里添加了 Kotlin 对 Android 的扩展,同时也添加了 Kotlin 的 Gradle 插件。
接下来就可以编写 Kotlin 代码了——等等,Android Studio 会帮我们生成一个MainActivity,你可以直接在菜单
Code -> Convert Java file to Kotlin file
将这个 Java 代码转换为 Kotlin 代码。截止到现在,你什么都不用做,程序就已经可以跑起来了。
2、完美为Java开发者打造
2.1 通用的集合框架
我们都知道 Jvm 上面的语言,像什么 Java、Groovy、Jython 啥的,都是要编成虚拟机的字节码的,一旦编成字节码,在一定程度上大家就都平等了。
英雄不问出身啊
有人做过一个非常形象的比喻:Java 虚拟机语言就是打群架。Kotlin 正是充分利用了这一点,它自己的标准库只是基于 Java 的语言框架做了许多扩展,你在Kotlin 当中使用的集合框架仍然跟你在Java当中一样。
举个例子,如果你想要在 Kotlin 中使用 ArrayList,很简单,Java 的 ArrayList 你可以随意使用,这个感觉跟使用 Java 没有任何区别,请看:
当然,Kotlin 标准库也对这些做了扩展,我们在享用 Java 世界的一切资源的同时,还能比原生 Java 代码更滋润,真是爽爆有木有:
2.2 与Java交互
Kotlin 的标准库更多的是对 Java 库的扩展,基于这个设计思路,你丝毫不需要担心 Kotlin 对 Java 代码的引用,你甚至可以在 Kotlin 当中使用 Java 反射,反正只要是 Java 有的,Kotlin 都有,于是有人做出这样的评价:
Kotlin 就是 Java 的一个扩展
这样说 Kotlin 显然是不公平的,但就像微信刚面世那会儿要为 QQ 接收离线消息一样,总得抱几天大腿嘛。
有关从 Kotlin 中调用Java的官方文档在此Calling Java code from Kotlin (https://kotlinlang.org/docs/reference/java-interop.html#static-methods-and-fields),其中最常见的就是 Getter / Setter 方法对应到 Kotlin 属性的调用,举个例子:
准备一个Java类
下面是Kotlin代码
所以我们在 Android 开发时,就可以这样:
- <ol class="linenums" style="box-sizing: border-box; border: 0px; margin: 0px 0px 10px 25px; padding: 0px; color: rgb(204, 204, 204);"><li class="L0" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px 0px 0px 1em; list-style: decimal;"><span class="pln" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">view</span><span class="pun" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">.</span><span class="pln" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">background </span><span class="pun" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">=</span><span class="pln" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);"> </span><span class="pun" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">...</span></li><li class="L1" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px 0px 0px 1em; list-style: decimal;"><span class="pln" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">textView</span><span class="pun" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">.</span><span class="pln" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">text </span><span class="pun" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">=</span><span class="pln" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);"> </span><span class="pun" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">...</span></li></ol>
反过来在 Java 中调用 Kotlin 也毫无压力,官方文档C alling Kotlin from Java 对于常见的情况作了比较详细的阐述,这里就不再赘述。
3、简洁,可靠,有趣
3.1 数据类
最初学 Java 的时候,学到一个概念叫 JavaBean,当时就要被这个概念给折磨死了。明明很简单的一个东西,结果搞得很复杂的样子,而且由于当时对于这些数据类的设计概念不是很清晰,因而也并不懂得去覆写诸如 equals 和 hashcode 这样重要的方法,一旦用到 HashMap 这样的集合框架,总是出了问题都不知道找谁。
Kotlin 提供了一种非常简单的方式来创建这样的数据类,例如:
- <ol class="linenums" style="box-sizing: border-box; border: 0px; margin: 0px 0px 10px 25px; padding: 0px; color: rgb(204, 204, 204);"><li class="L0" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px 0px 0px 1em; list-style: decimal;"><span class="pln" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">data </span><span class="kwd" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(167, 29, 93);">class</span><span class="pln" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);"> </span><span class="typ" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(0, 134, 179);">Coordinate</span><span class="pun" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">(</span><span class="pln" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">val x</span><span class="pun" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">:</span><span class="pln" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);"> </span><span class="typ" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(0, 134, 179);">Double</span><span class="pun" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">,</span><span class="pln" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);"> val y</span><span class="pun" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">:</span><span class="pln" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);"> </span><span class="typ" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(0, 134, 179);">Double</span><span class="pun" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">)</span></li></ol>
仅仅一行代码,Kotlin 就会创建出一个完整的数据类,并自动生成相应的 equals、hashcode、toString 方法。是不是早就受够了 getter和setter?反正我是受够了。
3.2 空安全与属性代理
第一次见到空类型安全的设计是在 Swift 当中,那时候还觉得这个东西有点儿意思哈,一旦要求变量不能为空以后,因它而导致的空指针异常的可能性就直接没有了。想想每次 QA 提的 bug 吧,说少了都得有三分之一是空指针吧。
Kotlin 的空安全设计,主要是在类型后面加?表示可空,否则就不能为 null。
- <ol class="linenums" style="box-sizing: border-box; border: 0px; margin: 0px 0px 10px 25px; padding: 0px; color: rgb(204, 204, 204);"><li class="L0" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px 0px 0px 1em; list-style: decimal;"><span class="pln" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">val anInt</span><span class="pun" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">:</span><span class="pln" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);"> </span><span class="typ" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(0, 134, 179);">Int</span><span class="pln" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);"> </span><span class="pun" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">=</span><span class="pln" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);"> </span><span class="kwd" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(167, 29, 93);">null</span><span class="pln" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);"> </span><span class="com" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(150, 152, 150);">// 错误</span></li><li class="L1" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px 0px 0px 1em; list-style: decimal;"><span class="pln" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">val anotherInt</span><span class="pun" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">:</span><span class="pln" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);"> </span><span class="typ" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(0, 134, 179);">Int</span><span class="pun" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">?</span><span class="pln" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);"> </span><span class="pun" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">=</span><span class="pln" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);"> </span><span class="kwd" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(167, 29, 93);">null</span><span class="pln" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);"> </span><span class="com" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(150, 152, 150);">// 正确</span></li></ol>
使用时,则:
而对于 Java 代码,比如我们在覆写 Activity 的 onCreate 方法时,有个参数 savedInstanceState:
- <ol class="linenums" style="box-sizing: border-box; border: 0px; margin: 0px 0px 10px 25px; padding: 0px; color: rgb(204, 204, 204);"><li class="L0" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px 0px 0px 1em; list-style: decimal;"><span class="kwd" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(167, 29, 93);">override</span><span class="pln" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);"> fun onCreate</span><span class="pun" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">(</span><span class="pln" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">savedInstanceState</span><span class="pun" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">:</span><span class="pln" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);"> </span><span class="typ" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(0, 134, 179);">Bundle</span><span class="pun" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">!)</span></li></ol>
这表示编译器不再强制 savedInstanceState 是否可 null,开发者在覆写时可以自己决定是否可 null。当然,对于本例,onCreate 的参数是可能为 null 的,因此覆写以后的方法应为:
- <ol class="linenums" style="box-sizing: border-box; border: 0px; margin: 0px 0px 10px 25px; padding: 0px; color: rgb(204, 204, 204);"><li class="L0" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px 0px 0px 1em; list-style: decimal;"><span class="kwd" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(167, 29, 93);">override</span><span class="pln" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);"> fun onCreate</span><span class="pun" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">(</span><span class="pln" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">savedInstanceState</span><span class="pun" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">:</span><span class="pln" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);"> </span><span class="typ" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(0, 134, 179);">Bundle</span><span class="pun" style="box-sizing: border-box; border: 0px; margin: 0px; padding: 0px; color: rgb(51, 51, 51);">?)</span></li></ol>
通常来讲,教科书式的讲法,到这里就该结束了。然而直到我真正用 Kotlin 开始写代码时,发现,有些需求实现起来真的有些奇怪。
还是举个例子,我需要在 Activity 当中创建一个 View 的引用,通常我们在 Java 代码中这么写:
在 Kotlin 当中呢?
每次用 aTextView 都要加俩!,不然编译器不能确定它究竟是不是 null,于是不让你使用。。这尼玛。。。到底是为了方便还是为了麻烦??
所以后来我又决定这么写:
这可如何是好??
其实 Kotlin 肯定是有办法解决这个问题哒!比如上面的场景,我们这么写就可以咯:
lazy 是 Kotlin 的属性代理的一个实例,它提供了延迟加载的机制。换句话说,这里的 lazy 提供了初始化 aTextView 的方法,不过真正初始化这个动作发生的时机却是在 aTextView 第一次被使用时了。lazy 默认是线程安全的,你当然也可以关掉这个配置,只需要加个参数即可:
好,这时候肯定有人要扔西红柿过来了(再扔点儿鸡蛋呗),你这 lazy 只能初始化 val 啊,万一我要定义一个 var 成员,又需要延迟初始化,关键还不为 null,怎么办??
lateinit 的使用还是有很多限制的,比如只能在不可 null 的对象上使用,比须为var,不能为 primitives(Int、Float之类)等等,不过这样逼迫你一定要初始化这个变量的做法,确实能减少我们在开发中的遗漏,从而提高开发效率。
|
请发表评论