1. 介绍

今年初,甲骨文再次对谷歌所谓的安卓侵权使用Java提起诉讼,要求后者赔偿高达90亿美元。随后便传出谷歌因此计划将主力语言切换到苹果主导的Swift,不过这事后来没了跟进。

但谷歌在这两天的I/O大会上宣布了新决定:Kotlin语言正式成为安卓开发的一级编程语言。资料显示,Kotlin由JetBrains公司开发,于2010年首次推出,次年开源。它与Java 100%互通,并具备诸多Java尚不支持的新特性,下一版的Android Studio(3.0)将提供支持。

去年底的时候接触到Kotlin,尝试将一个练手的Android项目由Java转为Kotlin实现并写了一篇文章(初次尝试用Kotlin实现Android项目)。既然现在Google正式发话了,作为一名Android开发者,先不说要不要替换Java,利用闲暇时间先系统地学习起来肯定是没错的。

关于Kotlin的各种介绍和教程在网上都可以找得到,下面附上几个链接供大家参考:

英文官网

中文版PDF下载

Kotlin in Chinese

Kotlin官网 Kotlin官网:中文

如何评价 Kotlin 语言?——知乎

Android开发必备知识:为什么说Kotlin值得一试

2. 简洁性

Google有意从Java转到Kotlin,当然不仅仅是因为甲骨文的官司,而且Kotlin是由JetBrains开发。这就不难猜到,Kotlin这门语言本身有胜过Java的地方,还不止一点点。

如果只是想体验Kotlin和Java的基本差异,选择Intellij IDEA就可以,新建、编译Java或者Kotlin项目速度较快;如果需要在Android项目中编写Kotlin代码,建议用Android Studio,虽然目前只能通过安装插件的形式来支持Kotlin,但使用起来毫无违和感。所以,电脑条件允许的话把两个开发环境都装上,以备不时之需。

这篇文章主要是通过测试代码来比较Kotlin和Java的几个不同点,利用IntelliJ IDEA建了一个Kotlin项目Kotlin0,之后关于纯Kotlin代码的项目会放在Github上的地址KotlinForJava,涉及Kotlin代码实现的Android项目会放在KotlinForAndroid。

教程写的再好,如果没有不断的编码积累,也只能学到表面的知识。另外,Kotlin官网提供了在线编码测试,懒得搭建环境的朋友可以直接在上面写代码和运行。

2.1 数据类定义

Java:

 public class Artist {
private long id;
private String name;
private String blog; public long getId() {
return id;
} public void setId(long id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getBlog() {
return blog;
} public void setBlog(String blog) {
this.blog = blog;
} @Override public String toString() {
return "Artist{" +
"id=" + id +
", name='" + name + '\'' +
", blog='" + blog + '\'''}';
}
}

属性+设置+获取+toString(),一个数据类的基本功能就有了,三个属性的类写了三十几行代码。

Kotlin:

 data class Artist(
var id: Long,
var name: String,
var blog: String)

上面类的定义在声明属性时,Kotlin采用默认访问修饰符public,而Java是private,如果kotlin也是private,那同样得定义一系列的set()/get(),不然在类外面无法通过对象获取属性。

除了代码量上的不同,还得关注data关键字和类名后面紧跟的属性列表。

先看参数列表,这里其实对应的是Java中的有参构造函数。如果Kotlin定义类时有参数列表,那么新建对象时必须传入形参值,不像Java有两种方式(1 先建一个无参对象,再通过set()来给属性赋值;2 直接通过参数列表创建对象)。但是使用过之后就会发现,Kotlin这种做法更简洁,且不会出现新建了对象而属性没有赋值的情况,也就更安全了。当然,Kotlin还是可以再定义其他的构造、初始化及一般功能性的方法。

创建对象

Java:

 Artist artist = new Artist(, "Dylan", "http://www.cnblogs.com/tgyf/");

Kotlin:

 var artist = Artist(, "Dylan", "http://www.cnblogs.com/tgyf/")

Kotlin对象创建不用加new关键字,而且语句后面不用加分号";"(即使加上也会被忽略)。

toString()

而类名前的data关键字,是显示声明该类是作为数据类使用,通过toString()打印的结果可以看出区别,打印语句:

 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,只会打印出一串数字(应该是类的内存地址),而不是当前对象的属性信息。

2.2 变量空安全

不管是C++的指针,还是Java的引用,因指向对象为null导致的问题一直困扰着我们。Kotlin提供了一种安全机制,尽量减少变量在使用前是null的情况。

Java:

 String str;
if (str != null) {
//do something
}

对于Java代码,编译器不会强制每次使用引用变量之前进行null判断,即异常往往会在运行时报出,但这正是危险所在。

Kotlin:

 var str1: String = null  //Null can not be a value of a non-null type String
var str2: String? = null //str2 can be null
var str3 = "testNull" //non-null--String type
var str4 = null //null
var str5: String //non-null--String type
str5 = "testNull" //assigned String value
var str6 //no type or initialization
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?的变量,访问属性时会涉及到问号和双感叹号两个操作符("?"和"!!"),前者表示执行后面代码前先检查变量赋值情况,后者表示不检查而直接访问属性(危险)。

要理解清楚,最好的方法就是让代码说话。

 var str2: String? = null
println("str2.length: " + str2.length) //compile error
println("str2?.length: " + str2?.length) //print null
println("str2!!.length: " + str2!!.length) //run exception
if (str2 != null) {
println("str2!!.length: " + str2!!.length) //don't run
}
str2 = "testNull" //assign
println("str2.length: " + str2.length) //print 8
println("str2?.length: " + str2?.length) //print 8
println("str2!!.length: " + str2!!.length) //print 8
if (str2 != null) {
println("str2!!.length: " + str2.length) //print 8
}

第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的。这种情况应该并不少见吧,那次此时"!!"就派上用场了。

先举一个简单粗暴的列子:

 var str: String? = null
//do something to assign str
val str2: String = str!!

当声明str的时候还需后面的处理结果给它赋值,而声明str2为非null,就必须以str!!的形式才能通过编译。

下面再给出Android中Application类单例化代码,就不做解释了。

 class App : Application() {
companion object {
private var instance: Application? = null
fun instance() = instance!!
}
override fun onCreate() {
super.onCreate()
instance = this
}
}

2.3 类方法扩展

这个特性支持在现有类的基础上扩展方法,特别是系统库中的类,因为如果是我们自定义的类,那么扩展和添加方法没有什么差别。

方法定义

 fun getArtict(): Artist? {
return null
}

Kotlin中是以fun关键字声明方法,没有返回值时不需要在方法名后面写任何类型,默认是Unit类型(可写可不写,但其和null不是一回事,所以不写返回值类型或者写了Unit后不能够返回null)。

扩展

 fun String.printStr() {
println("printStr: " + this)
} var str = "testExtend"
str.printStr()

上面代码为类String扩展了一个printStr(),这在Java中是不可能的。因为Java中如果既不能改变原有类,又想在其基础上添加方法,就得通过新建类来继承的方式。而现实是Java中只能是单继承,这个机会太珍贵了,更残酷的是有些类还是不能继承的。

代码第5-6行执行结果为:

printStr: testExtend

可见,通过this关键字即可获取到对象(调用者)的值。

 2.4 lambda表达式

这部分测试代码没有在分享的项目中,因为涉及到Android开发,需要在Android项目中才能编译或运行,可以参考这篇

下面以绑定控件,设置按钮点击事件监听,点击后改变文本显示为例。

Java:

 Button button = (Button) findViewById(R.id.button);
TextView text = (TextView) findViewById(R.id.text);
button.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View v) {
text.setText("Set text after click button");
}
});

做过Android开发的对这段代码太熟悉了,尽管目前已经出了很多开源库,比如ButterKnife等可以不必使用findViewById()而实现快速绑定,但毕竟还是需要手动绑定这一步。

Kotlin:

 button.setOnClickListener {
text.setText("Set text after click button")
text.text = "Set text after click button"
}

其中,button~R.id.button,第一个text~R.id.text,第二个text~TextView显示文本。第2-3行是设置文本的两种方式,Kotlin建议用更简洁的第二种.text,这也是文章开头定义数据类时属性采用默认访问修饰的原因,因为private属性就不能直接通过"."直接获取了。

如果遇到多个Button需要共享一个onClick()怎么办呢?Java代码就不给出了,来看Kotlin代码:

 button1.setOnClickListener(this)
button2.setOnClickListener(this)
button3.setOnClickListener(this) override fun onClick(view: View) {
val id = view.id
when (id) {
R.id.button1 -> selectImageBtn()
R.id.button2 -> clearImageBtn()
R.id.button3 -> sendBulletinBtn()
else -> { }
}
}

第1-3行,除了不需要调用findViewById()来获取控件,设置事件监听和Java是类似的;

第5-13行,重写关键字override,前面不必写"@"符号,用when、->及else组合来替代原先的switch、case及default,再也不用为每种case的结尾写上break。

3. 总结

这篇文章主要是介绍Kotlin,给出目前较好的学习资源,通过数据类定义、变量空安全、类方法扩展及lambda表达式四个方面来和Java做了一个简单的比较,顺带提了Kotlin其他一些基础知识。算是对Kotlin的一个入门,之后会比较系统地学习,并结合Android项目来进行练习与总结。

上面提到的知识只是Kotlin的九牛一毛,关于在Android开发中的环境配置及更进一步的由Java转为Kotlin,可以参考这篇

最后得说明一点,也是打算利用Kotlin开发Android应用时必须搞清楚的:Kotlin是基于JVM的。也就是说,尽管编码上和Java相比更简洁,大大提高了开发的效率,但还是和Java一样是运行在JVM中。而且,Kotlin和Java是百分百兼容的,即一个项目中可以同时存在它们的代码,还可以交互。

Kotlin入门第一课:从对比Java开始的更多相关文章

  1. Kotlin入门第二课:集合操作

    测试项目Github地址: KotlinForJava 前文传送: Kotlin入门第一课:从对比Java开始 初次尝试用Kotlin实现Android项目 1. 介绍 作为Kotlin入门的第二课, ...

  2. emacs 入门第一课:Emacs里的基本概念

    Table of Contents 无聊的开场白 buffer(缓冲区) window(窗口)与frame Emacs的mode Emacs Lisp 函数function.命令command.键绑定 ...

  3. Asp.Net Web API 2(入门)第一课

    Asp.Net Web API 2(入门)第一课   前言 Http不仅仅服务于Web Pages.它也是一个创建展示服务和数据的API的强大平台.Http是简单的.灵活的.无处不在的.你能想象到几乎 ...

  4. Android入门第一课之Java基础

    通知:由于本周六场地申请没通过,所以本周的培训临时取消. 今天给大家带来的是Android入门的第一课,由于教室申请的不确定性,因此,每次培训的内容都会在博客先提前释放出来.首先Android的APP ...

  5. Kotlin学习第一课:从对比Java开始

    1. 介绍 今年初,甲骨文再次对谷歌所谓的安卓侵权使用Java提起诉讼,要求后者赔偿高达90亿美元.随后便传出谷歌因此计划将主力语言切换到苹果主导的Swift,不过这事后来没了跟进. 但谷歌在这两天的 ...

  6. Java学习之路(一):日常第一课,认识JAVA

    Java的介绍 语言的起源 Java是SUN(Stanford University Network 斯坦福大学网络公司) 1995年推出的一门高级编程语言. Java名称的来源: Java最初是被命 ...

  7. Spring入门第一课:Spring基础与配置Bean

    1.入门 Spring是简化java开发的一个框架,其中IoC和AOP是Spring的两个重要核心.由于Spring是非侵入性的,通过Ioc容器来管理bean的生命周期,还整合了许多其他的优秀框架,所 ...

  8. 1.Java第一课:初识java

    今天也算是正式地开始学习Java了,一天学的不是太多,旨在入门了解Java.还好现在学的都是基础,也能赶得上进度,希望以后能一直保持这种精神状态坚持学下去.下面就简单来说说今天所学的内容吧. 1计算机 ...

  9. 《java入门第一季》之java语法部分小案例

    到这里举一个小例子把语法部分梳理一下,下一节开始进入java正式基础--面向对象的学习整理. 案例: /* 某个公司采用公用电话传递数据信息,数据是小于8位的整数,为了确保安全, 在传递过程中需要加密 ...

随机推荐

  1. Struts与jsp+javabean+servlet区别

    采用Struts框架技术和直接使用jsp+javabean技术开发各有什么优势?各自的缺点又是什么? 在Javaweb开发领域存在2种开发模式, 模式一,jsp+javabean 模式二,jsp+ja ...

  2. ASP.NET Web API中通过URI显示实体中的部分字段

    有时候我们可能不想显示某个实体中的所有字段.比如客户端发出如下请求: locaohost:43321/api/groups/1/items?fields=idlocaohost:43321/api/g ...

  3. PropertyGrid 重难点总结 转

    PropertyGrid的界面组成与不同部分的名称如下图所示. 本博文不算是自己写作的,只是将PropertyGrid中的几项十分有用的功能的应用方面的文字进行一下总结,希望以后大家对Property ...

  4. c# 以换行(\r\n)拆分字符串

    c# 以换行(\r\n)拆分字符串 字符串数组形式: string[] striparr = strip.Split(new string[] { "\r\n" }, String ...

  5. [数据结构与算法分析(Mark Allen Weiss)]不相交集 @ Python

    最简单的不相交集的实现,来自MAW的<数据结构与算法分析>. 代码: class DisjSet: def __init__(self, NumSets): self.S = [0 for ...

  6. RHEL磁盘修复

    0. 1.基础工具:e2label /device/xxx [new label name]   显示/设定设备的label名称 2.e2fsck 修复工具,用-b 指定备用的superblock位置 ...

  7. MySQL5.7在JSON解析后丢失小数部分的Bug

    在MySQL Bugs上提交了 https://bugs.mysql.com/bug.php?id=84935 . 已经在MySQL8.0.1中修复 重现步骤 -- Prepare the table ...

  8. Rplidar学习(四)—— ROS下进行rplidar雷达数据采集源码分析

    一.子函数分析 1.发布数据子函数 (1)雷达数据数据类型 Header header # timestamp in the header is the acquisition time of # t ...

  9. php static 变量的例子

    class test { public static function a(){} public function b(){} } $obj = new test; 调用 代码 test::a(); ...

  10. Android UI系列-----CheckBox和RadioButton(1)

    主要记录一下CheckBox多选框和RadioGroup.RadioButton单选框的设置以及注册监听器 1.CheckBox 布局文件: <LinearLayout xmlns:androi ...