一周前开始学习kotlin,现在已经在项目中开发使用了。我目前负责的项目老代码非常多,之前的java代码已经有十多万行了。所以即便使用了kotlin,也只是在新建文件的代码里使用,老代码继续用java。kotlin的好处就是完全兼容java,java调用kotlin,kotlin基本上无阻碍。
先说说为什么要转kotlin
Google已经明确kotlin为第一官方语言。我相信Google的决意,就像当初毫不犹豫的抛弃eclipse转向as。kotlin已经是不可避免的大势所趋了。
Kotlin与java代码完全兼容。使用kotlin就好像只是增加了一个库。打个比方,Rxjava比较难上手吧,不懂Rxjava的人看Rxjava像看天书,更不知从何写起。kotlin学起来比Rxjava简单的多。
作为一名程序猿,我是非常追求代码的简洁高效。在代码简洁上,kotlin简直是神器,太和我心意了。由于要兼容低版本,之前就一直在用java的lambda表达式插件,但是很多java8的特性还是用不了。kotlin全部能做到,而且更好。在易理解的基础上,能用1行代码搞定的事我绝不会写两行。用了kotlin,我自己写的很多基础函数都用不着了。真正的Enjoy Coding!
也说一下目前遇到的一些问题
由于集成了kotlin包,apk会增大约0.5M。这看你的项目是否能接受了,我觉得还行。
目前没发现什么大坑。在java-kotlin互相调用时要小心,koltin如果不注明,是强制不为空的。如果你的java代码有良好的习惯,出入参都注明了@Nullable @Nonnull,就没什么问题了
安装与配置
参考kotlin官网
我的android studio 没有升级3.0预览版,使用的还是2.3版本。
首先更新kotlin语言插件
屏幕快照 2017-05-30 下午6.17.38.png
工程里添加如下插件配置
//必须 apply plugin: 'kotlin-android' //快捷的findviewfindid操作,建议加上 apply plugin: 'kotlin-android-extensions'
然后是依赖路径
buildscript { //可能由于androidstudio版本原因,老提示我这里使用1.1.2-3版本,最新应该是1.1.2-4,根据提示来即可 ext.kotlin_version = '1.1.2-3' repositories { jcenter() //阿里的maven仓国内镜像,如果gradle下载很慢,建议使用该仓库 maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' } mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:2.3.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version" } }
最后是编译依赖的仓库,用着这个就行了。
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
注意几点:
如果之前使用了依赖于注解的库如Dragger,databinding等,需要额外的配置,详情请参考官网
Anko 是一个封装了很多android操作的库,比如ui操作、异步操作、数据库操作等等。不过我目前觉得还用不着,就没加。感兴趣的同学可以自己去添加。
ok, 可以进入kotlin世界了!
我主要是参考了这本书 《Kotlin for Android developers》 ,加上一些自己的理解。内容不多,看几遍就记得差不多了。刚学习kotlin时可以下载下来,使用时随时查阅。
Koltin的类
kotlin的变量申明 val 是不可变变量,相当于java里的final。var是可变变量,一般情况尽量使用val 。如果使用var一般都需要判空
val i = 7 //自动类型推导 val str:String = "hello"//也可以显式的指明类型 var name: String? = null//可变变量一般要注意使用安全操作符? ,使用时都要加上? val length = name?.length
这里有个小坑,如果没加?传入了空参数是,kotlin会强制判空抛异常。
只要是Java语言有交互的参数,除非是100%确定不为空,否则请务必都加上安全操作符?。
类继承
class CustomView(context: Context?, attrs: AttributeSet?) : View(context, attrs)
构造函数
额外添加一个属性name
class CustomView(val name:String, context: Context?, attrs: AttributeSet?) : View(context, attrs)
添加其他构造函数
class CustomView(context: Context?, attrs:AttributeSet?) : TextView(context, attrs){ constructor(name: String, context: Context?, attrs:AttributeSet?):this(context, attrs) { text = name } }
另外,kotlin不需要new操作符了,创建类直接调用其构造方法,就像使用普通函数那样。
val view = CustomView(context, attrs)
Kotlin的方法
方法申明差不多,动手写一下很快就明白了。
默认参数特别好用! 终于可以不用写一大堆同名函数了
动态方法扩展相当于将一大堆Util换种写法
比如异步加载图片的封装,java这样写
public static void setImageUriAsync(@NonNull ImageView iv, String uri, boolean round) { ... } public static void setImageUriAsync(@NonNull ImageView iv, String uri) { setImageUriAsync(iv, uri, false); }
kotlin这样写:
fun ImageView.setImageUrlAsync(url: String?, isRound: Boolean = false) { ... }
调用时就当做imageview的普通方法使用即可
iv.setImageUrlAsync(url) iv.setImageUrlAsync(url, true)
注意一点
在java里,所有的方法都要在声明在类里,一般一个类也只能有一个pulic方法。kotlin的方法可以独立放在类外面,左右全局使用,比如前面提到ImageView扩展方法就要单独放在类外部。kotlin一个文件可以申明多个全局方法,声明多个public类。就是说,很多东西都能放在kotlin一个文件里。
Koltin的Null处理
使用安全操作符?可以省略很多无意义判空操作
不多说,直接上代码,一看就都明白了。
以前这样写
if (adapter != null) { Listlist = adapter.getDatas(); if (list != null) { for (CmdNotice it :list) { if (it.getId() == notice.getId()) { it.setStatus(notice.getStatus()); adapter.notifyDataSetChanged(); break; } } } }
现在这样写
adapter?.datas?.firstOrNull({ it.id == notice.id })?.let { it.status = notice.status adapter?.notifyDataSetChanged() }
静态常量,静态函数,单例
如果一个类里都是静态常量和静态函数,直接在最外面写
object Const{ val MSG_1 = 1 fun getCurrentDate():Date{ return Date() } }
如果是在普通类里有静态变量,以及单例,这样写
class MyCache{ companion object { val TYPE = 1 //常量 val instance = MyCache() //单例 } }
这样调用
MyCache.TYPE MyCache.instance
kotlin的inline(内联)函数
内联函数与一般的函数不同,在编译时会做替换,少了普通函数调用的压栈出栈,更高效。因为是替换,所以可以识别传入的泛型
比如
inline fungetClassName():String{ return T::class.java.name }
直接这样调用
val name = getClassName()
可以传入代码块,比如一个异步线程操作的代码块
object MyExecutor{ val instance: ExecutorService = Executors.newCachedThreadPool() } inline fun doSync(crossinline block: () -> Unit) { MyExecutor.instance.execute { block() } } //调用如下。 传入的block代码块可以直接放在函数后面的{}里。 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main) doSync{ //Code: Request network } }
Kotlin基础库里大量的便捷操作以及Anko库里很多操作都是使用inline函数完成的。常用的when, apply, let,以及对list数组封装的各种操作firstOrNull,filter等都是内联函数完成的。
Koltin函数需要传入的代码块可以直接放在函数名的大括号里,不需要再用小扩号包起来。这样的话,很多调用就非常简洁
比如Anko库里的async, uiThread ,异步操作的写法比Rxjava简单的多!
async{ //Do step1 cost time uiThread { toast("complete step1") } //Do step2 cost time uiThread { toast("complete step2")} }
而且其写法很巧妙,只保存了外部Activity的弱引用,如果在异步执行过程中Activity销毁了uiThread则不会调用,防止内存泄漏。
FindViewById
由于有了插件kotlin-android-extensions.
Activity里可以这样写,不需要在额外申明TextView变量了。所以注意xml文件里id的命名使用驼峰写法
setContentView(R.layout.main) tvTitle.setOnClickListener { toast("hello") }
Adapter里这样写, ViewHolder完全不需要了
view.tvName.text = notice.name view.tvNumber.text = "" view.ivIcon.setImageUrlAsync(icon, true)
其他
注意在kotlin中== 相当于java中的equal函数,=== 相当于java中的==。不过一般情况加==就够了。kotlin中没有基本类型,所有的如Int,Long都是对象。
kotlin中没有三元操作符。可以用if else,或者 ?:代替。
java:
String a = result?"true":"false"
kotlin:
val a = if (result) "true" else "false"
?:表示先判断前面是否为空,为空的话就返回后面的
java:
String b = (a==null)?a:"true"
kotlin:
val b=a?:"true"
因为都是基于JVM,Kotlin的所有操作java当然都能做到,但是kotlin更简洁容易理解!
先到这里,如有问题欢迎指正,大家共同进步!