Java转Kotlin学习(一)
至今都不敢相信,我有一天会写kotlin的文章,虽然Kotlin在Google I/O中的推出并表示也作为安卓开发的官方语言,Kotlin的发展也是越来越迅猛,但Java仍然是很多开发者的首选语言,之前习也惯了java开发,Kotlin这个词仅存在和朋友的聊天记录中。
在这里插入图片描述
但是公司现在的项目都是kotlin开发,项目开发过程中和其他时间也学习了一段时间,发现还是挺舒服的,所以在此分享些心得吧。
真正接触才发现呢,有挺多优点的
1.语法简便
Kotlin的语法简单,Java语言有严格的数据类型,转换类型就会很繁琐,同时Kotlin也不用像Java那样中不断的非空判断,没有语言基础的学起来也很轻松。
2.提高开发效率
凭借简洁直观的语法,Kotlin提高了团队的效率,编写和部署程序需要更少的代码行和时间。
3.强大的兼容
a.与 Java 可互操作,可以在 Kotlin 中编写新模块,可以与现有 Java 代码协同工作;
b.最新版本的Kotlin与之前的所有版本都是反向兼容;Kotlin 兼容所有 Java 库和框架以及JVM;
c.可以与 Gradle 或 Maven 构建系统进行整合;
d.idea和android studio都有对应Java转kotlin的插件,原有的Java代码可以直接转换。
接下来的介绍就从和java的对比开始
类定义
java
public class Artist {
2 private long id;
3 private String name;
4 private String blog;
5
6 public long getId() {
7 return id;
8 }
9
10 public void setId(long id) {
11 this.id = id;
12 }
13
14 public String getName() {
15 return name;
16 }
17
18 public void setName(String name) {
19 this.name = name;
20 }
21
22 public String getBlog() {
23 return blog;
24 }
25
26 public void setBlog(String blog) {
27 this.blog = blog;
28 }
29
30 @Override public String toString() {
31 return "Artist{" +
32 "id=" + id +
33 ", name='" + name + '\'' +
34 ", blog='" + blog + '\'''}';
35 }
36 }
之前的话属性+设置+获取+toString(),一个数据类的基本功能就有了,三个属性的类写了三十几行代码
kotlin
1 data class Artist(
2 var id: Long,
3 var name: String,
4 var blog: String
5 )
对象创建
Java:
1 Artist artist = new Artist(1, "Dylan", "http://www.cnblogs.com/tgyf/");
Kotlin:
1 var artist = Artist(1, "Dylan", "http://www.cnblogs.com/tgyf/")
Kotlin对象创建不用加new关键字,而且语句后面不用加分号";"(即使加上也会被忽略)。
toString()
而类名前的data关键字,是显示声明该类是作为数据类使用,通过toString()打印的结果可以看出区别,打印语句:
1 println("artist.toString(): " + artist.toString())
不加data结果:
artist.toString(): Artist@61bbe9ba
加上data结果:
artist.toString(): Artist(id=1, name=Dylan, blog=http://www.cnblogs.com/tgyf/)
可以看到toString()是Kotlin自动生成的,如果类声明不加data,只会打印出一串数字(应该是类的内存地址),而不是当前对象的属性信息。
空安全
空指针这个错误不用多说,太常见了,Kotlin提供了一种安全机制,尽量减少变量在使用前是null的情况。
Java:
1 String str;
2 if (str != null) {
3 //do something
4 }
对于Java代码,编译器不会强制每次使用引用变量之前进行null判断,即异常往往会在运行时报出,但这正是危险所在。
Kotlin:
1 var str1: String = null //Null can not be a value of a non-null type String
2 var str2: String? = null //str2 can be null
3 var str3 = "testNull" //non-null--String type
4 var str4 = null //null
5 var str5: String //non-null--String type
6 str5 = "testNull" //assigned String value
7 var str6 //no type or initialization
8 var str7: String? = "testNull"
结合代码中的注释,我们来看这四行代码想表达的意思。
第1行,编译错误,kotlin规定如果显式指明了str1的类型,这里是String,声明时必须同时指定是否允许为空值(null),不加问号"?"表示不允许为null;
第2行,编译通过,作第一行代码的另一种情况,加了问号,并赋值为null;
第3行,编译通过,隐式赋值为"testNull",Kotlin会自动推断出str3类型为String,之后便不可再更改了,即不可再赋值为1这种整形数据;
第4行,编译通过,隐式赋值为null,那么str4就一直为null了;
第5-6行,编译通过,前者只是指定类型,没有赋值;后者赋予str5 String类型值"testNull"同样不能赋值为其他类型值;
第7行,编译错误,既没有指定类型,也没有隐式地进行初始化,错误的原因应该是编译器不知道str6类型是什么,不能对其分配空间;
第8行,不需多解释,str7可为null,同时赋值为"testNull";
注意:此文为了格式统一,没有将编译或运行出错的代码注释,分享的项目代码中是可以正常编译并运行的。
解释完变量定义时关于空的概念,接下来就该看看这种保护机制能否真的让我们省心。就拿获取字串的长度为例,Kotlin中String类有个length属性,即调用方式为strObject.length。
有两种形式定义的变量不用担心(1 类型为String且不允许为null;2 类中不包含length属性), 理由很简单,前者不会出现null异常,后者获取length属性在编译阶段就会出错,或者说在敲完代码时编译器就会标红提示了。所以,String类型但允许为null的才需要我们关注,因为这时候有可能出现运行时异常。
对于声明为String?的变量,访问属性时会涉及到问号和双感叹号两个操作符("?"和"!!"),前者表示执行后面代码前先检查变量赋值情况,后者表示不检查而直接访问属性(危险)。
要理解清楚,最好的方法就是让代码说话。
1 var str2: String? = null
2 println("str2.length: " + str2.length) //compile error
3 println("str2?.length: " + str2?.length) //print null
4 println("str2!!.length: " + str2!!.length) //run exception
5 if (str2 != null) {
6 println("str2!!.length: " + str2!!.length) //don't run
7 }
8 str2 = "testNull" //assign
9 println("str2.length: " + str2.length) //print 8
10 println("str2?.length: " + str2?.length) //print 8
11 println("str2!!.length: " + str2!!.length) //print 8
12 if (str2 != null) {
13 println("str2!!.length: " + str2.length) //print 8
14 }
第2行,编译错误,因为之前只是将str2声明为可以是null同时赋值为null,所以紧接着访问其length属性是不允许的;
第3行,输出"null",加了问号就会先检查str2的赋值情况,如果是null,就不继续执行后半部分(.length),直接返回null;
第4行,运行异常,不检查的后果就是通过null引用去访问length属性;
第5-7行,不会执行到if代码块中,这里用了类似Java中的做法;
第9行,输出"8",到这里,相比能体会到Kotlin的智能之处了,在第八行对str2赋值之后,就不会再像第二行那样报编译错误了;
第10-14行,不需多解释,不为null的str2,通过三种方式均可访问length属性;
那么这里有一个疑问,用"!!"来访问属性是不明智的选择,好像"?"更稳妥一些?毕竟后者在变量是否null的情况下都能做出相应的处理。我所能想到的需要用"!!"的场景之一是:当一个变量在声明时不能马上初始化,而在真正用到时又必须是非null的。这种情况应该并不少见吧,那次此时"!!"就派上用场了。
先举一个简单粗暴的列子:
1 var str: String? = null
2 //do something to assign str
3 val str2: String = str!!
当声明str的时候还需后面的处理结果给它赋值,而声明str2为非null,就必须以str!!的形式才能通过编译。
下面再给出Android中Application类单例化代码,就不做解释了。
1 class App : Application() {
2 companion object {
3 private var instance: Application? = null
4 fun instance() = instance!!
5 }
6 override fun onCreate() {
7 super.onCreate()
8 instance = this
9 }
10 }
类方法扩展
这个特性支持在现有类的基础上扩展方法,特别是系统库中的类,因为如果是我们自定义的类,那么扩展和添加方法没有什么差别。
方法定义
1 fun getArtict(): Artist? {
2 return null
3 }
Kotlin中是以fun关键字声明方法,没有返回值时不需要在方法名后面写任何类型,默认是Unit类型(可写可不写,但其和null不是一回事,所以不写返回值类型或者写了Unit后不能够返回null)。
扩展
1 fun String.printStr() {
2 println("printStr: " + this)
3 }
4
5 var str = "testExtend"
6 str.printStr()
上面代码为类String扩展了一个printStr(),这在Java中是不可能的。因为Java中如果既不能改变原有类,又想在其基础上添加方法,就得通过新建类来继承的方式。而现实是Java中只能是单继承,这个机会太珍贵了,更残酷的是有些类还是不能继承的。
代码第5-6行执行结果为:
printStr: testExtend
可见,通过this关键字即可获取到对象(调用者)的值。
lambda表达式
这部分测试代码没有在分享的项目中,因为涉及到Android开发,需要在Android项目中才能编译或运行,可以参考这篇。
下面以绑定控件,设置按钮点击事件监听,点击后改变文本显示为例。
Java:
1 Button button = (Button) findViewById(R.id.button);
2 TextView text = (TextView) findViewById(R.id.text);
3 button.setOnClickListener(new View.OnClickListener() {
4
5 @Override
6 public void onClick(View v) {
7 text.setText("Set text after click button");
8 }
9 });
做过Android开发的对这段代码太熟悉了,尽管目前已经出了很多开源库,比如ButterKnife等可以不必使用findViewById()而实现快速绑定,但毕竟还是需要手动绑定这一步。
Kotlin:
1 button.setOnClickListener {
2 text.setText("Set text after click button")
3 text.text = "Set text after click button"
4 }
其中,buttonR.id.button,第一个textR.id.text,第二个text~TextView显示文本。第2-3行是设置文本的两种方式,Kotlin建议用更简洁的第二种.text,这也是文章开头定义数据类时属性采用默认访问修饰的原因,因为private属性就不能直接通过"."直接获取了。
如果遇到多个Button需要共享一个onClick()怎么办呢?Java代码就不给出了,来看Kotlin代码:
1 button1.setOnClickListener(this)
2 button2.setOnClickListener(this)
3 button3.setOnClickListener(this)
4
5 override fun onClick(view: View) {
6 val id = view.id
7 when (id) {
8 R.id.button1 -> selectImageBtn()
9 R.id.button2 -> clearImageBtn()
10 R.id.button3 -> sendBulletinBtn()
11 else -> { }
12 }
13 }
第1-3行,除了不需要调用findViewById()来获取控件,设置事件监听和Java是类似的;
第5-13行,重写关键字override,前面不必写"@"符号,用when、->及else组合来替代原先的switch、case及default,再也不用为每种case的结尾写上break。
总结
这篇文章先写这么多吧,提到的知识仅是Kotlin的九牛一毛,我觉得作为开发者,学习是很有必要的,在熟练掌握了某种语言的基础上,学习新的语言,有助于提升个人竞争力。