虽然Anko效率很高,代码简洁,清爽直观,但是目前还有很多坑,主要包括:
1.AS并不支持直接预览Anko界面,虽然有个Anko DSL Preview插件,但是需要make才能刷新,而且和现在的AS不兼容。
2.如果要在多版本中动态替换外部资源,需要用动态类加载才能实现,无法借用资源apk实现。
3.不方便根据view的id去即时引用view控件(R文件和inflate这时反而愈加灵活)。
另外,Anko还在异步、日志、Toast、对话框、数据库等方面提供优化服务,能否采用就看自身需要了。
相对Java而言,主要的变化有这么几条:
1.没有“;”
在Kotlin语法里,代码行不需要用“;”结尾,什么都不写就好
2.重要的“:”
在Java里,“:”主要在运算符里出现(for/switch/三元运算符等)。
在Kotlin里,“:”的地位大大提升了,它的用途非常广泛,包括:
定义变量类型
var name:String="my name" //变量name为String类型
定义参数的类型
fun makeTool(id:Int){ //参数id为Int类型
}
定义函数的返回值
fun getAddr(id:Int):String{ //返回值为String类型
}
声明类/接口的继承
class KotlinActivityUI :AnkoComponent
{//继承AnkoComponent接口
使用Java类
val intent = Intent(this, MainActivity::class.java) //需要用::来使用Java类,注意是两个“”
3.没有“new”
Kotlin实例化一个对象时不需要new关键字
var list=ArrayList()
4.变量、常量、类型推断
用var定义变量(像js)
var name:String="my name"
用val定义常量(相当于final)
val TAG:String="ClassName"
上面两个例子用:String来定义了数据类型,这个是可以省略的,Kotlin支持类型推断,这两句话你可以写成
var name="my name"
val TAG="ClassName"
5.初始化和延迟加载
在Java里,我们可以定义一个变量,但是并不赋值(int和boolean会有默认值)
但是Kotlin里必须为变量赋值,如果只写一个变量,却不赋值,像下面这样:
var name
编译器会报错,提示你未初始化,你必须赋值为0或者null,或者别的什么值。
不过,我们有时候就是不能在定义变量时就初始化它,比如在Android中我们经常预定义一个View控件而不初始化,但是直到onCreate或onCreateView时才初始化它。
针对这种情况,Kotlin提供了懒加载lazy机制来解决这个问题,在懒加载机制里,变量只有在第一次被调用时,才会初始化,代码需要这样写
在初次调用时初始化变量的lazy机制
lazy只适用于val对象,对于var对象,需要使用lateinit,原理是类似的,只是代码需要这样写
var变量使用lateinit机制
6.空指针安全
在Kotlin里,可以用“?”表示可以为空,也可以用“!!”表示不可以为空。
空指针安全并不是不需要处理空指针,你需要用“?”声明某个变量是允许空指针的,例如:
var num:Int?=null
声明允许为空时,不能使用类型推断,必须声明其数据类型
空指针虽然安全了,但对空指针的处理还是要视情况而定,有时候不处理,有时候做数据检查,有时候还需要抛出异常,这三种情况可以这样写:
val v1 =num?.toInt() //不做处理返回 null
val v2 =num?.toInt() ?:0 //判断为空时返回0
val v3 =num!!.toInt() //抛出空指针异常(用“!!”表示不能为空)
更多空指针异常处理,有一篇NullPointException 利器 Kotlin 可选型介绍的比较全面,值得借鉴
7.定义函数
在Kotlin语法里,定义函数的格式是这样的
fun 方法名(参数名:类型,参数名:类型...) :返回类型{
}
所以,一般来说,函数是这样写的
fun getAddress(id:Int,name:String):String{
return"got it"
}
由于Kotlin可以对函数的返回值进行类型推断,所以经常用“=”代替返回类型和“return”关键字,上面这段代码也可以写成
fun getAddress(id:Int,name:String)={ //用“=”代替return,返回String类型则交给类型推断
"got it" //return被“=”代替了
}
如果函数内代码只有一行,我们甚至可以去掉{}
fun getAddress(id:Int,name:String)="got it" //去掉了{}
}
函数也允许空指针安全,在返回类型后面增加“?”即可
fun getAddress(id:Int,name:String) :String?="got it"
有时候,函数的返回类型是个Unit,这其实就是Java中的void,表示没有返回
fun addAddress(id:Int,name:String):Unit{ //相当于java的void
}
不过,在函数无返回时,一般不写Unit
fun addAddress(id:Int,name:String){ //相当于java的void
}
8.用is取代了instance of
代码很简单
if(obj is String)...
9.in、区间和集合
Kotlin里有区间的概念,例如1..5表示的就是1-5的整数区间
可以用in判断数字能否在某个区间
if(x in 1..5){ ...//检查x数值能否在1到5区间
可以用in判断集合中能否存在某个元素
if(name in list){...//检查list中能否有某个元素(比Java简洁多了)
可以用in遍历整个集合
for(i in 1..5){ ...//遍历1到5
for(item in list){...//遍历list中的每个元素(相当于Java的for(String item : list))
另外,in在遍历集合时功能相当强大:
在遍历集合时,可以从第N项开始遍历
for(i in 3..list.size-2){...相当于for (int i = 3; i <= list.size()-2; i++)
可以倒序遍历
for(i in list.size downTo 0) {...相当于for (int i = list.size(); i >= 0; i--)
可以反转列表
for(i in (1..5).reversed())
可以指定步长
for(i in 1.0..2.0 step 0.3) //步长0.3
Kotlin里的集合还都自带foreach函数
list.forEach {...
10.用when取代了switch
switch在Java里一直不怎么给力,在稍老一些的版本里,甚至不支持String
Kotlin干脆用强大的when取代了switch,具体用法如下
when的用法
代码中的参数类型Any,相当于Java中的Obejct,是Kotlin中所有类的基类,至于object关键字,在Kotlin中另有用处...
11.字符串模板
在Java里使用字符串模板没有难度,但是可读性较差,代码一般是
MessageFormat.format("{0}xivehribuher{1}xhvihuehewogweg",para0,para2);
在字符串较长时,你就很难读出字符串想表达什么
在kotlin里,字符串模板可读性更好
"${para0}xivehribuher${para1}xhvihuehewogweg"
12.数据类
数据类是Kotlin相对Java的一项重大改进,我们在Java里定义一个数据Model时,要做的事情有很多,例如需要定义getter/setter(虽然有插件代写),需要自己写equals(),hashCode(),copy()等函数(部分需要手写)
但是在Kotlin里,你只需要用data修饰class的一行代码
data class Client(var id:Long,var name:String,var birth:Int,var addr:String)
Kotlin会自动帮你实现前面说的那些特性。
数据模型里经常需要一些静态属性或方法,Kotlin可以在数据类里添加一个companion object(伴随对象),让这个类的所有对象共享这个伴随对象(object在Kotlin中用来表示单例,Kotlin用Any来表示所有类的基类)
添加companion obejct
13.单例模式
单例是很常见的一种设计模式,Kotlin干脆从语言级别提供单例,关键字为object,如果你在扩展了Kotlin的IDE里输入singleton,IDE也会自动帮你生成一个伴随对象,也就是一个单例
单例类
如果一个类需要被定义为class,又想做成单例,就需要用上一节中提到的companion object
例如,如果我们用IDE新建一个blankFragment,IDE会自动帮我们写出下面的代码,这本来是为了解决Fragment初始化时传值的问题,我们注意到她已经使用了companion object单例
自动生成的代码
如果我们修改一下newInstance这个函数
改成单例
那么,我们用
BlankFragment.newInstance()
就可以调用这个fragment的单例了
14.为已存在的类扩展方法和属性
为了满足开放封闭原则,类是允许扩展,同时严禁修改的,但是实现扩展并不轻松,在Java里,我们需要先再造一个新的类,在新类里继承或者引用旧类,然后才能在新类里扩展方法和属性,实际上Java里层层嵌套的类也非常多。
在Kotlin里,这就简洁优雅地多,她允许直接在一个旧的类上做扩展,即便这是一个final类。
例如,Android中常见的Toast,参数较多,写起来也相对繁琐,我们一般是新建一个Util类去做一个相对简单的函数,比如叫做showLongToast什么的,我们不会想在Activity或Fragment中扩展这个函数,因为太麻烦,我们需要继承Activity做一个比如叫ToastActivity的类,在里面扩展showLongToast函数,然后把业务Activity改为继承这个ToastActivity...
在Kotlin里,我们只需要这样写
扩展函数
就完成了Activity类的函数扩展,我们可以在Activity及其子类里随意调用了
调用扩展函数
需要注意的是,你无法用扩展去覆盖已存在的方法,例如,Activity里已经有一个onBackPressed方法,那么你再扩展一个Activity.onBackPressed方法是无用的,当你调用Activity().onBackPressed()时,它只会指向Activity本身的那个onBackPressed方法。
我们还可以用类似的方式去扩展属性
扩展属性
不过,Kotlin的扩展其实是伪装的,我们并没有真正给Activity类扩展出新的函数或属性,你在A类里为Activity扩展了函数,换到B类里,你就找不到这个函数了。
这是因为,Kotlin为类扩展函数时,并没有真的去修改对应的类文件,只是借助IDE和编译器,使他看起来像扩展而已。
所以,如果类的某些函数只在特殊场景下使用,可以使用灵活简洁的扩展函数来实现。
但是,如果想为类永久性地添加某些新的特性,还是要利用继承或者装饰模式(decorator)。
不过,Kotlin里对于类的家族定义和Java有所不同,我们来看一下
15.类的家族结构
Kotlin关于类的家族结构的设计,和Java基本相似,但是略有不同:
Object:取消,在Java里Object是所有类的基类,但在Kotlin里,基类改成了Any
Any:新增,Kotlin里所有类的基类
object:新增,Kotlin是区分大小写的,object是Kotlin中的单例类
new:取消,Kotlin不需要new关键字
private: 仍然表示私有
protected: 类似private,在子类中也可见
internal: 模块内可见
inner:内部类
public: 仍然表示共有,但是Kotlin的内部类和参数默认为public
abstract:仍然表示抽象类
interface:仍然表示接口
final:取消,Kotlin的继承和Java不同,Java的类默认可继承,只有final修饰的类不能继承;Kotlin的类默认不能继承,只有为open修饰的类能继承
open:新增,作用见上一条
static:取消!Java用static去共享同一块内存空间,这是一个非常实用的设计,不过Kotlin移除了static,用伴随对象(前面提到过的compaion object)的概念替换了static,伴随对象其实是个单例的实体,所以伴随对象比static愈加灵活一些,能去继承和扩展。
继承:在Kotlin里,继承关系统一用“:”,不需要向java那样区分implement和extend,在继承多个类/接口时,中间用“,”区分即可,另外,在继承类时,类后面要跟()。所以在Kotlin里,继承类和接口的代码一般是这样的:
class BaseClass : Activity(), IBinder{ //示例
16.构造函数
在Java里,类的构造函数是这样的
pulic 类名作为函数名 (参数) {...}
Java里有时会重载多个构造函数,这些构造函数都是并列的
在Kotlin里,类也可以有多个构造函数(constructor),但是分成了1个主构造函数和N个二级构造函数,二级构造函数必须直接或间接代理主构造函数,也就是说,在Kotlin里,主构造函数有核心地位
主构造函数一般直接写在类名后面,像这么写
class ClientInfo(id:Long,name:String,addr:String){
这其实是个缩写,完全版本应该是
class ClientInfo constructor(id:Long,name:String,addr:String){
主构造函数的这个结构,基本决定了,在这个主构造函数里,没法写初始化代码...
而二级构造函数必须代理主构造函数,写出来的效果是这样的
二级构造函数
17.初始化模块init
上一节提到过,主构造函数里不能写代码,这就很麻烦了,不过还好,Kotlin提供了初始化模块,基本上就是用init修饰符修饰一个{},在类初始化时执行这段儿代码,代码像这样写就行
初始化模块
18.其他
Kotlin还有很多其他的语言特性,本文主要是为了建立对Kotlin的大概印象,更多细节就不再列举了,建议仔细阅读Kotlin官方文档,并且多动手写一些代码。