当下互联网大数据云计算时代,数以百万计的应用程序在服务器、移动手机端上运行,其中的开发语言有很大一部分是用流行软件界20多年的、强大稳定的主力的编程语言Java编写。
如果我们用一辆汽车来比喻编程语言的话,Java 拥有一个快速、可靠的发动机,但防抱死刹车系统,动力转向系统却不是那么地完全可控。驾驶起来得小心翼翼地检查可能出现的空指针,处理检查异常,重复生成冗长而单调的样板代码行等等。而编程语言的防危性(safety)和安全性(security)却又是至关重要的。
要是有一门语言,既能继承Java的所有优点特性及其背后强大完备的生态库,又能更加简单、安全可控,那真是再好不过了。
我们很高兴地看到,Kotlin就是这样一门语言。Kotlin是一门非研究性的语言,它是一门非常务实的工业级编程语言,它的使命就是帮助程序员们解决实际工程实践中的问题。使用Kotlin 让 Java程序员们的生活变得更好,Java中的那些空指针错误,浪费时间的冗长的样板代码,啰嗦的语法限制等等,在Kotlin中统统消失。Kotlin 简单务实,语法简洁而强大,安全且表达力强,极富生产力。
本章我们先简单介绍Kotlin语言的发展历史和语言特性,然后简述为什么要去学习Kotlin语言。最后,简要介绍JVM语言家族。
Kotlin是一种基于JVM的静态类型编程语言。Kotlin从一开始推出到如今,已经有六年,2016年官方正式发布首个稳定版本,发展简史如下:
2011年7月,JetBrains推出Kotlin项目。
2012年2月,JetBrains以Apache 2许可证开源此项目。
2016年2月15日,Kotlin v1.0(第一个官方稳定版本)发布。
2017 Google I/O 大会,Kotlin “转正”。
Kotlin 具备类型推断、多范式支持、可空性表达、扩展函数、模式匹配等诸多下一代编程语言特性。
Kotlin的编译器kompiler可以被独立出来并嵌入到 Maven、Ant 或 Gradle 工具链中。这使得在 IDE 中开发的代码能够利用已有的机制来构建,可以在新环境中自由使用。
让我们从Hello World开始。与 C/C ++/Java 一样,Kotlin 程序的入口点是一个名为“main”的函数,它传递一个包含任何命令行参数的数组。
package com.easy.kotlin
fun main(args: Array) {
println("Hello,World!")
}
上面的代码简单说明如下:
1.Kotlin中的包package的使用跟Java基本相同。
2.Kotlin 变量声明 args: Array
3.与Scala和Groovy一样 ,代码行末尾的分号是可选的。在大多数情况下, 编译器根据换行符就能够推断语句已经结束。
4.Kotlin中使用fun关键字声明函数(方法),充满乐趣的fun。
人们为什么喜欢Kotlin?Kotlin为什么值得我们去学习?下面是一个不完全的清单列表:
Kotlin的优势是,既有Java的完整生态(Kotlin 完全无缝使用各类Java API框架库),又有现代语言的高级特性 (语法糖)。
Kotlin 语言的设计的初衷之一是为了 JetBrains 团队的内部使用,旨在帮助公司降低成本。用过IntelliJ IDEA的都知道JetBrains 团队出品,皆是良品。毫无疑问,Kotlin的设计是务实的 。发展和促进 Kotlin 的好处大于其成本, 在这个过程中, Kotlin 已经演变成了一个 JetBrains 的效率工具。其强烈的务实取向强烈地吸引了一大批Java程序员。
Kotlin 也成为 JetBrains 工具生态系统中重要的一员。希望在未来几年内 Kotlin 成为主要的非 Java的 JVM语言,甚至有一天成为下一个Java语言。可以预测的是,Kotlin 将大大提升整个Java 互联网开发者的效率和质量。
Kotlin语言的特性可以简单概括如下:
1.实用主义( Pragmatic ):务实、注重工程实践性。我们经常会听到或者看到人们说编程是数学,或者是工程,是艺术,是科学,这些说法都是很有道理的。Kotlin是一门偏重工程实践与艺术上的极简风格的语言。
2.极简主义( Minimalist ):语法简洁优雅不啰嗦,类型系统中一切皆是引用(reference)。
3.空安全(Null Safety):有一个简单完备的类型系统来支持空安全
4.多范式 (multi-paradigm ):同时一等支持 OOP 与 FP 编程范式。各种编程风格的组合可以让我们更加直接地表达算法思想和解决问题的方案,可以赋予我们思考上更大的自由度和灵活性。
5.可扩展:直接扩展类的函数与属性(extension functions & properties)。这与我们在Java中经常写的util类是完全不一样的体验!Kotlin是一门非常注重用户体验的语言。
6.高阶函数与闭包(higher-order functions & closures)。Kotlin 的类型中,函数类型(function type)也是一等类型( first class type),在Kotlin中我们可以把函数当成值进行传递。这直接赋予了 Kotlin 函数式编程的特性。
使用Kotin可以写出一些非常优雅的代码。
7.支持快速实现 DSL。有了扩展函数、闭包等特性的支持,使用Kotlin实现一个DSL将会相当简单方便。
“我们认为Kotlin的定位是一种现代化工业语言:它专注于代码重用和可读性的弹性抽象,以及面向早期错误侦测,和明确捕获维护与清理的意图,这些问题的静态类型安全性。Kotlin最重要的使用场景之一是对于一个庞大的Java代码库,其开发者需要一个更棒的语言:你能够将Java和Kotlin自由混合,迁移可以是渐进式的,不需要一下子对整个代码库进行改变。”
“Kotlin旨在成为一种面向工业的面向对象语言,而且是一种比Java更好的语言,但仍然可以与Java代码完全互操作,允许企业逐步从Java迁移到Kotlin 。”
Andrey Breslav, Kotlin创始人
编程的真正的问题在于,如何把人类脑子里对问题的解决方案“具化”到机器世界,而这个“具化”的过程正是编程语言所要表达的东西。如何富有表现力并且安全简洁地表达,这是所有编程语言所要解决的问题。让人类能够尽可能“自然地”和计算机进行沟通交流,这一直是促使人们提高编程语言抽象层次的主要目标之一。很显然的一个事实就是,跟用机器语言写的低层次结构代码相比,用编译语言写成的高层次结构代码更接近于人一般思考时所用的概念。
Kotlin设计了一个“归一化”的类型系统(一切类型皆是引用类型),纯天然地设置了一道空指针的屏障,使得Kotlin比Java更加安全可靠。Kotlin还引入了类型推断、一等支持函数式编程、Lambda、高阶函数、类的扩展函数与属性、DSL等诸多特性,使得我们可以编写简单、优雅且高效的代码,更加专注地投入在业务逻辑的实现上。
优秀的程序员当然会选择使用Kotlin这些更加先进的特性,因为它们毫无疑问有助于更直接地表达观点,而且也没有额外的开销。何乐而不为呢?
“工欲善其事必先利其器”。本节我们简单介绍一下学习Kotlin的工具平台。
如果你想快速体验一下Kotlin,只需要浏览器打开云端IDE https://try.kotlinlang.org/
在这里你可以快速感受到Kotlin语言到底长什么样子。但是,这里不支持代码智能提示以及自动补全等功能。
有时候我们并不需要打开IDE来做一些事情。打开 IDE 是件很麻烦的事情,在某些场景下,我们比较喜欢命令行。
使用命令行环境,我们可以方便地使用Kotlin REPL(Read-Eval-Print-Loop,交互式编程环境)。REPL可以实时编写Kotlin代码,并查看运行结果。通常REPL交互方式可以用于调试、测试以及试验某种想法。
如果你想本地快速测试一个简短的Kotlin代码,可以使用命令行REPL。Kotlin是运行在JVM环境下的语言。首先我们要有JDK环境(Java环境配置此处省略)。
目前,Kotlin最新正式发布的版本是1.1.50。首先,去下载Kotlin运行环境安装包: https://github.com/JetBrains/kotlin/releases/download/v1.1.50/kotlin-compiler-1.1.50.zip
解压完kotlin-compiler-1.1.50.zip,放到相应的目录下。然后配置系统环境变量:
export KOTLIN_HOME=/Users/jack/soft/kotlinc
export PATH=$PATH:$KOTLIN_HOME/bin
执行source ~/.bashrc
, 命令行输入kotlinc
, 即可进入 Kotlin REPL界面
$ kotlinc
Welcome to Kotlin version 1.1.50 (JRE 1.8.0_40-b27)
Type :help for help, :quit for quit
>>> println("Hello,World!")
Hello,World!
>>> import java.util.Date
>>> Date()
Wed Jun 07 14:19:33 CST 2017
我们如果想拥有学习Kotlin的相对较好的体验,就不建议使用eclipse了。毕竟Kotlin是JetBrains家族的亲儿子,跟Intelli IDEA是血浓于水啊。
我们使用IDEA新建gradle项目,选择Java,Kotlin(Java)框架支持,如下图:
新建完项目,我们写一个HelloWorld.kt类
package com.easy.kotlin
import java.util.Date
import java.text.SimpleDateFormat
fun main(args: Array) {
println("Hello, world!")
println(SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Date()))
}
直接运行HelloWorld.kt,输出结果如下
Hello, world!
2017-05-29 01:15:30
现在的编程语言已经足够多了,为什么我们还需要更多的语言?Java已经如此流行普及足够强大了,为什么我们还需要Kotlin、Scala这样的语言呢?
其实,如果我们仔细想想,这个问题本身的逻辑就不成立。你想想,我们能这样说吗——煎鸡排已经足够好吃了,为什么我们还要去吃煎牛排呢?
从最早的机器语言(01机器码,汇编语言)到高级语言(Lisp,Basic,Pascal,C,C++,Java,Haskell等),再到现代编程语言(Go,Swift,Scala,Kotlin等),编程语言百花齐放、百家争鸣,其演化史可谓是蓬勃繁荣。
最早的编程语言就是01机器码(Machine Code)。那个时候的程序员要会用0和1表示一切!
后来人们聪明地想到,是不是可以把一些常用的指令操作单独抽象出来,用特定的关键字来映射01机器码序列?这就是汇编语言,这可以算作是编程语言过程中的第一次抽象封装。也许,汇编语言的主要意义不在于它与机器语言之间并不显著的差别,而是这样一个关键的想法:程序完全可以在不同的层次上编制!人们可以用机器语言写一个“翻译程序”, 从而使得我们可以在一个更高层次进行编程。
后来汇编用久了,人们也逐渐发现了使用汇编过程中的问题:可移植性差。汇编代码中是大量的字节指令码,而且还必须一步一步地告诉计算机每一步要怎么做,一个步骤出错,执行结果就是程序员们意想不到的!使用汇编语言编程,极易在子程序调用过程中导致寄存器内容错误,而且调试程序也很困难。程序在正常运行时,我们基本不太去过多地关心和想象它的活动结构和层次空间。而只有当它出现bug或者崩溃的时候,我们才竭尽全力地在不同层次上思考和想象我们的程序运行的具体细节。而这其中的出错信息将变得至关重要。例如,一个除法操作,遇到除数为0的情况,程序将暂停运行,并把错误抛出给程序员。下面是不同层次上的debug信息:
机器语言层:程序运行异常终止于11110000010001001地址;
汇编语言层:程序运行异常终止于DIV指令;
编译语言层:程序运行异常终止于代码行256 (a+b)/c 处。
从上面的信息中,显而易见的是,层次越高,越容易被我们人类大脑所理解。
在高级语言中,所有参数都必须严格匹配其类型,这样就不会出现寄存器内容错误的情况。高级语言就是为了解决汇编语言的这些问题进行的更高一层的抽象与封装。这层封装就是编译器。编译器所要解决的问题就是,如何构造一个系统,使它可以接收当前层次的描述,然后从中生成另一个层次上的描述。通常来说,设计一门语言相对容易,而实现这门语言的编译器则是比较复杂的。编译器制定了一系列的协议规范、语法规则等,只要程序员们按照这个协议规范来编程,编译器就可以将高级语言的源代码翻译成对应CPU指令集上的汇编语言代码。高级语言不要求程序员掌握计算机的硬件运行,只要写好上层代码。著名的高级语言有 BASIC、FORTRAN(公式翻译)、 COBOL(通用商业语言)、 C、 PASACL、ADA 语言等。
尽管 C 语言 (1972, Dennis MacAlistair Ritchie,启发语言有 B语言、汇编、ALGOL68等)已经足够普及且非常强大,后来还是出现了针对 C 语言进行改进和功能扩展的新语言 C++ ( 1979,Bjarne Stroustrup ),C++ 集成了 C 语言的特性,然后加入了面向对象程序设计的特性支持。和汇编语言不同的是,在C语言的语句和机器语言的指令之间不再是简单的一一对应的关系,不过毫无疑问的是,仍然有一种从C语言代码到机器语言代码的映射关系,但是这种关系要比从汇编到机器语言之间的关系要复杂多了。而完成这个映射过程翻译的程序,我们就称之为“编译器”。
而C/C++语言最大的一个问题就是“一切都会尖叫着停止”,因为它们使用了直接操纵内存的指针。一旦因为使用指针而出现了内存错误,系统核心就会崩溃。
有没有一种语言可以控制这样的风险呢?
后来的Java(1995,James Gosling)继承了 C/C++ 语言的优点,摒弃了C++里的指针操作、手动管理内存、多继承等诸多复杂而并不实用功能特性,引入了划时代的 Java虚拟机(JVM,Java Virtual Machine),JVM是一种虚拟的计算机,从结构上看,它与实际的计算机架构相似,JVM的作用是使得一台实际的机器能够运行Java 字节码(bytecode)。引用James Gosling的话就是
“大部分人大谈特谈JAVA语言,这对于我来说也许听起来很奇怪,但是我无法不去在意。JVM才是Java生态系统的核心啊。
我真正关心的是Java虚拟机的概念,因为是它把所有的东西都联系在了一起;是它造就了Java语言;是它使得事物能在所有的异构平台上得到运行;也还是它使得所有类型的语言能够共存。”
首先,JVM实现了Java的可移植性。
另外,JVM里面实现了一个垃圾收集器(GC,Garbage Collector)来管理内存,GC 对保证系统的可靠性和安全性非常实用有益。
同时,JVM还奠定了一个庞大的语言生态的基础。
Java是互联网时代当之无愧的最流行的开发语言。经过20多年的积累和沉淀,Java生态拥有了很多优秀的开源社区,如Apache和Spring。有了这些框架,我们可以更加专注业务的实现。
Java语言也有不好的一面。我们简单列举如下。
1.检查异常(Checked Exceptions)。检查异常会在编译时强制执行try catch处理,同时还需要进行某种排序处理。 检查异常是一个失败的实践,几乎所有的主要API提供者都反对可检查异常。Kotlin中摒弃了检查异常。
2.基本类型和数组。Java的这个设计保留了字节码的底层细节,违反了“凡事皆为对象”原则。泛型无法包容基本类型就是一个经典的例子。这也使得Java的类型系统显得不是那么地简单统一。比较好的方案是,源代码不用直接使用基本类型或者数组,编译器(或者JVM)来决定是否可以帮你对其进行优化。Kotlin正是这么做的。
3.静态变量(Static)。静态方法经常会导致需要显式的定义接口,从而使得API更加复杂。一个更好的办法就是采用单例对象,单例对象在大多数情况下表现都跟静态对象差不多,只不过也可以像一个对象一样被传递而已。Kotlin中提供了 object 单例对象。
4.泛型。Java泛型本身就很复杂,当使用? exends和? super等变种句型时就变得尤其复杂。非常容易搞错。这个问题在《Effective Java》一书中提出了PECS(Producer extends Consumer super)的建议,Kotlin直接使用了这个方案。
5.空指针异常(NPE)。Java中我们不得不写一堆防御代码来避免令人头疼的NPE。Kotlin中引入了可空类型与安全调用符、Elvis操作符等特性实现空安全。我们将在第3章中介绍。
6.一堆getter/setter单调冗长的样板代码。例如下面的Person Bean类
class PersonJava {
Integer id;
String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public PersonJava(Integer id, String name) {
this.id = id;
this.name = name;
}
}
在Kotlin中是这样子的:
data class PersonKotlin(val id: Int, val name: String)
7.不容易传递函数。Java中没有提供一等函数类型,函数式编程(FP)只能通过使用接口类型以及多态特性“曲线”来实现。Java会将每一个算法(方法)都放入类中,这样的限制会出现这样的荒唐事:我们只是想要实现一个函数算法,而这个时候我们必须还得整出一个类出来放置这样的方法;同样,如果在其它地方要调用这个方法,我们也必须通过创建该类来实现调用。在Kotlin中直截了当地提供了一等函数类型(First-Class Function Type)。跟普通类型一样,函数类型可以作为值来传递,也可以作为返回值。
还有其它的经验教训,上面所述只是其中的一部分。
不可否认的是, C、 C++ 和 Java 都是非常优秀的编程语言。但是事物总是不断发展变化的。就像 C++ 是对 C 的继承与发展,Java 是对 C++ 的继承与改造,而Kotlin也是对Java 的继承与变革。
这里是一张来自Java官网文档(http://docs.oracle.com/javase/8/docs/)里面的一张 Java 技术模块架构图
为了在JVM上正确运行我们的程序,我们只需要能够按照规范生成正确的 class 文件,然后加载到JVM中执行文件中指定的操作字节指令码(byte code)即可。
在过去20多年的发展历程中(1991-2017,如果算上最初的称之为oak语言的Java前生的话),Java语言、JVM、API库和框架、应用工具和Web服务器的速度、稳定性和功能方面却一直在提升,现在已被公认为是开发高端服务器。
JVM最初是为了支持java编程语言。然而,随着时间的流逝,越来越多的语言被改编或设计运行在JVM上。除了java语言,比较知名的JVM上的编程语言还有:Groovy、Scala、Clojure等。
JVM上主流编程语言历史时间轴概览:
计算机中的所有问题 , 都可以通过向上抽象封装一层来解决。
Java虚拟机对各个平台而言,实质上是各个平台上的一个可执行程序。例如在windows平台下,java虚拟机对于windows而言,就是一个java.exe进程而已。
通常情况下,在JVM平台上语言从源代码编译到JVM上执行的整体过程如下图所示
其中,运行在JVM上的字节码文件是不依赖于硬件和操作系统的二进制格式的文件。依赖硬件和操作系统的部分,由JVM分别在这些平台上来实现。例如JDK 8
我们经常说的Java语言是平台无关的,跨平台的。其实这是针对从Java/Scala/Kotlin/Groovy等的源代码到JVM字节码这一层是平台无关的。
但是,真正到了把JVM字节码通过解释器映射到不同平台(操作系统,CPU硬件架构)上,JVM就必须针对各个平台实现一套解释器。只是这一层通过抽象封装,对 Java/Scala/Kotlin/Groovy 程序员而言已经完全透明,无需再做相关的工作而已。
下一代普遍可接受语言(next mass-appeal language)中,人的因素应该起到重要作用。在功能方面,应该具备诸如以下特性:
语言设计其实堪比艺术品设计,每个人的口味跟审美都有各自的风格与特征,所以实现出一门好的编程语言确实不容易。
人的生命只有一次。生命太短暂,所以不要去做一些重复无聊的事情。能交给计算机做的,就尽量交给计算机去做。此乃人类进行计算机编程的滥觞之地。
未来人工智能将取代大部分的重复手工劳动。将大大解放人类的劳动力,从而使得人类能够花更多的时间和精力,去创造去创新。而人工智能的本质,就是对人类智能的抽象建模。我们人类写的操作系统、浏览器、办公软件、画图设计工具、3D建模软件、电商系统、金融平台、社交APP,不就是另一种层次上的人工智能吗?这些东西,背后都是01的映射。当然,01背后是物理层次的,量子微观的世界了,更加奥妙无穷。
纵览整个计算机的发展史,最重要的思想非“抽象”莫属。
一层层的抽象封装了实现的细节,计算机开疆扩土,南征北战,发展到了今天蔚为壮观的互联网,云计算,大数据,机器智能的时代。
同时,也使得程序员写代码,从最初的拿着符号表在纸袋上打孔,到使用近似自然语言的高级编程语言来编程(当然背后少不了编译器、解释器,还有的是先通过虚拟机中间字节码这一层,再通过解释器映射到机器码,最后在硬件上作高低电平的超高频率的舞蹈),以及当今各种库API、框架、集成开发工具集,智能化的编码提示,代码生成等等技术,使得我们现在程序员,能更多的去关注问题本身以及逻辑的实现。
从只有少数技术人会用的命令行的Unix、DOS操作系统,到人性化的GUI图形界面操作系统,再到移动互联网时代的智能设备,计算机与互联网越来越融入到人类生活的方方面面。
正如解决数学问题通常我们会谈“思想”,诸如反证法、化繁为简等,解决计算机问题也有很多非常出色的思想。思想之所以称为思想,是因为“思想”有拓展性与引导性,可以解决一系列问题。
解决问题的复杂程度直接取决于抽象的种类及质量。过将结构、性质不同的底层实现进行封装,向上提供统一的API接口,让使用者觉得就是在使用一个统一的资源,或者让使用者觉得自己在使用一个本来底层不直接提供、“虚拟”出来的资源。