携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情
前言
作为一个Javaer,学习Kotlin的过程能用一个四字成语概括:照猫画虎。相信很多Javaer也深受同感。这么这么个功能Java是怎么写的,然后按照这么个写法寻找Kotlin中对应的写法进行“直译”。这样子的“直译”或许也能够实现功能,但势必不能体会到Kotlin的优胜之处,只能是成为流于表面的、披着Javaer皮的Kotliner。和翻译类似,直译只能体现表层而不能体现灵魂。只有从Kotlin的灵魂出发,才能更好地掌握Kotlin。
变量与常量
这里就不说Kotlin和Java最基础的声明差别了,相信用过Kotlin的朋友们都是知道的。
首先来说说Java中的final关键字。
在Java中,final表明不可变,赋值一次后不能进行修改。但在实际的使用中,大多数人都不会单独使用final这个关键字,为什么?麻烦呗。而在Kotlin中,则使用了val关键字来实现只读声明且val关键字与可变变量关键字var同级别。也就是说,可变与不可变的声明并没有什么不同,这增加了声明只读变量的便利性。在Kotlin中有这么一个非明文惯例,在声明变量时,统一先使用val进行声明,在后续编写过程中需要对其进行修改时才改为var。这样子做并不如Java中多写一个final关键字那么繁琐。正是因为如此,对变量的值增加限制,能让代码更加可靠。
但val的用法还不仅仅如此。当val处于顶级(也就是kotlin文件中第一级的大括号内)时,能对其定义getter。what?不是说final了吗?定不定义getter有什么区别呢?这时候就体现出val与final不一样的地方了。在这种情况下,用val声明的成为只读属性,能够通过自定义的getter返回不相同的值,例如:
class MainActivity{
val currentTime: Long
get() = System.currentTimeMillis()
}
此时虽然不能对currentTime进行二次赋值,但每次获取时值都不相同。
那么写Java的时候,final关键字用得最多的情景是什么呢?自然是static final组合拳了。static final表面这个量为静态常量。但在Kotlin中,“静态”这一概念貌似是被抛弃了。既然如此,如果我们需要定义这样一个“静态常量”那应该怎样写呢?
虽然Kotlin中没有了静态常量这个概念,但我们起码希望看到的是有一个替代写法能够让调用看起来和Java中的静态常量是一样的。Java中调用静态常量的写法是:
View.GONE
也就是类名.静态常量名。在Kotlin中,使用伴生对象(companion object)就可以达到相同的调用形式。但其中的含义则并不相同。下面会继续探讨这个话题。
了解Kotlin的人可能会说了,Kotlin不是还有const关键字吗?这不就是常量的意思吗?确实,const关键字是用来标识常量的,但这个常量是编译期常量。const关键字只能修饰基本类型和String类型并且不能自定义getter。
话虽如此,但静态方法和静态常量在Kotlin中有一个更为推荐的实现方法——顶层声明(top-level declaration)。即将方法和常量的声明放在class之外,这样做的效果跟静态方法和静态常量的效果一样,甚至调用的时候连类名都不用写了,因为这样声明的方法和常量是直接隶属于package而不是某一个类的。
这样子的话,在Kotlin中编写工具类就更加方便了。直接新建一个kt文件,里面没有类,全是顶层声明的工具方法。这样不仅调用时不需要xxxUtil.xxx,还能全局使用。
object
在Java中,Object是所有类的父类(注意是大写),Kotlin中,object的作用则更加符合这个词的意思——对象(注意是小写)。object在Kotlin中的地位提升了,不再是类而是关键字,所以它是小写的。(PS:在Kotlin中所有类都继承于Any类,类似Java中的Object类,但是不是完全相同我没怎么了解过不敢说。)
object关键字和class关键字的级别是一样的,class在哪用,object就可以在哪用。在我看来,object比较有用的是两个方面(当然还有什么匿名对象,不是不重要,只是我暂时还没用得上):
单例模式
在Java中,单例模式的创建需要新建一个类,持有一个该类的对象,将构造方法标记为private,暴露一个方法获取单例对象(通常是getInstance)。但在Kotlin中,只需要将该类的class关键字换成object关键字就可以实现单例模式了。
object xxxManager{
fun method(){}
val a = 1
}
调用:
xxxManager.method()
但是object没有构造方法,也就是说,如果初始化时依赖于参数的话,这种方法是不可行的。
伴生对象
上面说了,object可以和class的用法差不多,class有内部类,object自然也有内部对象了。内部对象的写法如下:
class A{
object B{
//B就是A的内部对象
val c = 1
}
}
上面的对象B其实可以做到一开始说的“静态”效果,只是调用起来样子和Java的有些区别:
//期望调用的形式为A.c
//实际形式为
A.B.c
而companion关键字的作用则是将该内部对象的名称等同于外部类的名称,并省略显示(不管是声明还是调用都可以省略)。如果用伴生对象改写一下A类:
class A{
companion object{//省略了内部类的名称
val c = 1
}
}
//调用时则为
A.c //调用时也省略了内部类的名称
总而言之,伴生对象和普通内部对象的区别就是没啥区别,只是增加了便利性。从这里也能看出来,其实Kotlin中实现的“静态”本质上和Java中的“静态”并不是相同的。
object是可以有父类或实现接口的,这里就不展开了(其实就是我不会……)。
后言
有错误或者不足之处欢迎友善指出,共同进步。