让我们从一个小例子开始,来看看 Kotlin 代码长什么样子。这个例子定义了一个 Person 类来表示“人”,创建一个“人”的集合,查找其中年纪最大的人,并打印结果。尽管这是非常小的一段代码,从中也可以看到 Kotlin 许多有趣的特性。
我们对其中的一些特性做了标记,以便你可以方便地在本书后续的内容中找到它们。
代码简要地进行了解释,但是如果有些内容你现在还无法理解,请不要担心,稍后我们会详细讨论。
如果你想尝试运行这个例子,最简单的方法是使用 http://try.kotl.in 的在线 Playground。输入示例代码并单击 Run 按钮,代码将会执行。
代码清单 1.1 Kotlin 初体验
你声明了一个简单的数据类,它包括了两个属性:name和age。age属性默认为null(如果没有指定)。在创建“人”的列表时,你省略了Alice的年龄,所以这里年龄使用了默认值null。然后你调用了maxBy函数来查找列表中年纪最大的那个“人”。传递给这个函数的lambda表达式需要一个参数,使用it作为这个参数的默认名称。如果age属性为null,Elvis运算符(?:)会返回零。因为Alice的年龄没有指定,Elvis运算符使用零代替了它,所以Bob幸运地成了年纪最大的人。
喜欢这样的代码吗?继续读下去,你将会学习到更多,并成为一名 Kotlin 专家。我们希望不久之后,在你自己的项目中也能看到这样的代码,而不只是在书上。
Kotlin的主要特征
你大概已经知道了 Kotlin 是一种怎样的语言,让我们更加深入地了解一下它的关键属性。首先,我们来看看你能用 Kotlin 创造哪些种类的应用程序。
目标平台 :服务器端、Android 及任何 Java 运行的地方
Kotlin 的首要目标是提供一种更简洁、更高效、更安全的替代 Java 的语言,并且适用于现今使用 Java 的所有环境。Java 是一门非常受欢迎的语言,它广泛地应用于不同的环境 :小到智能卡 (JavaCard 技术 ),大到 Google、Twitter、LinkedIn 和其他这种规模的互联网公司运行的最大的数据中心。在这些地方,使用 Kotlin 可以帮助开发者在实现目标的同时减少代码并避免麻烦。
Kotlin 最常见的应用场景有 :
- 编写服务器端代码(典型的代表是 Web 应用后端)
- 创建 Android 设备上运行的移动应用
但 Kotlin 还有其他用武之地。例如,可以使用 Intel Multi-OS Engine((https:// software.intel.com/en-us/multi-os-engine)让 Kotlin 代码运行在 iOS 设备上。还可以使用 Kotlin 和 TornadoFX(https://github.com/edvin/tornadofx)以及 JavaFX(http://mng.bz/500y) 一起来构建桌面应用程序。
除了 Java 之外,Kotlin 还可以编译成 JavaScript,允许你在浏览器中运行 Kotlin 代码。但截止本书撰写时,对 JavaScript 的支持仍在 JetBrains 内部探索并进行原型开发,这超出了本书的范围,而其他一些平台也在考虑支持 Kotlin 的未来版本。
正如你所看到的,Kotlin 的目标平台是相当广泛的。Kotlin 并没有被限制在单一的问题域,也没有被限制在解决软件开发者面临的某一类型的挑战。相反,对所有开发过程中涌现的任务,Kotlin 都提供了全面的生产力提升。它借助支持特定领域或编程范式的库,提供了卓越的集成水准。接下来让我们来看看 Kotlin 作为一种编程语言的关键特质。
静态类型
Kotlin 和 Java 一样是一种静态类型的编程语言。这意味着所有表达式的类型在编译期已经确定了,而编译器就能验证对象是否包含了你想访问的方法或者字段。
这与动态类型的编程语言形成了鲜明的对比,后者在 JVM 上的代表包括 Groovy 和 JRuby。这些语言允许你定义可以存储任何数据类型的变量,或者返回任何数据类型的函数,并在运行时才解析方法和字段引用。这会减少代码量并增加创建数据结构的灵活性。但它的缺点是,在编译期不能发现像名字拼写错误这样的问题,继而导致运行时的错误。
另一方面,与 Java 不同的是,Kotlin 不需要你在源代码中显式地声明每个变量的类型。很多情况下,变量类型可以根据上下文来自动判断,这样就可以省略类型声明。这里有一个可能是最简单的例子 :
val x = 1
在声明这个变量时,由于变量初始化为整型值,Kotlin 自动判断出它的类型是Int。编译器这种从上下文推断变量类型的能力被称作类型推导。
下面罗列了一些静态类型带来的好处 :
- 性能——方法调用速度更快,因为不需要在运行时才来判断调用的是哪个方法。
- 可靠性——编译器验证了程序的正确性,因而运行时崩溃的概率更低。
- 可维护性——陌生代码更容易维护,因为你可以看到代码中用到的对象的类型。
- 工具支持——静态类型使 IDE 能提供可靠的重构、精确的代码补全以及其他特性。
得益于 Kotlin 对类型推导的支持,你不再需要显式地声明类型,因此大部分关于静态类型的额外冗长代码也就不复存在了。
当你检视 Kotlin 类型系统的细节时,你会发现许多熟悉的概念。类、接口以及泛型和 Java 非常接近,所以大部分的 Java 知识可以很容易地转移到 Kotlin。然而,也会有一些新概念出现。
其中最重要的概念是 Kotlin 对可空类型的支持,通过在编译期检测可能存在的空指针异常,它让你可以写出更可靠的程序。
另一个 Kotlin 类型系统的新概念是对函数类型的支持。要搞清楚这一点,我们先要了解函数式编程的主要思想,以及 Kotlin 是如何支持这种编程风格的。
函数式和面向对象
作为一个 Java 开发者,你一定对面向对象编程的核心概念烂熟于胸,但函数式编程对你来说却可能很新鲜。函数式编程的核心概念如下 :
- 头等函数——把函数(一小段行为)当作值使用,可以用变量保存它,把它当作参数传递,或者当作其他函数的返回值。
- 不可变性——使用不可变对象,这保证了它们的状态在其创建之后不能再变化。
- 无副作用——使用的是纯函数。此类函数在输入相同时会产生同样的结果,并且不会修改其他对象的状态,也不会和外面的世界交互。
函数式编程风格的代码能给你带来什么好处 ? 首先,简洁。函数式风格的代码 比相应的命令式风格的代码更优雅、更简练,因为把函数当作值可以让你获得更强 大的抽象能力,从而避免重复代码。
假设你有两段类似的代码,实现相似的任务(例如,在集合中寻找一个匹配的 元素)但具体细节略有不同(如何判断元素是匹配的)。可以轻易地将这段逻辑中公 共的部分提取到一个函数中,并将其他不同的部分作为参数传递给它。这些参数本身也是函数,但你可以使用一种简洁的语法来表示这些匿名函数,它被称作 lambda 表达式 :
函数式编程风格的代码带来的第二个好处是多线程安全。多线程程序中最大的错误来源之一就是,在没有采用适当同步机制的情况下,在不同的线程上修改同一份数据。如果你使用的是不可变数据结构和纯函数,就能保证这样不安全的修改根 本不会发生,也就不需要考虑为其设计复杂的同步方案。
最后,函数式编程意味着测试更加容易。没有副作用的函数可以独立地进行测试,因为不需要写大量的设置代码来构造它们所依赖的整个环境。
一般来说,函数式编程风格可以在任何编程语言中使用(包括 Java),它的很多主张都被认为是良好的编程风格。然而并不是所有的语言都提供了语法和库支持,让我们可以毫不费力地使用这种风格。例如,Java 8 之前的 Java 版本都缺少了这种支持。Kotlin 拥有丰富的特性集从一开始就支持函数式编程风格,包括 :
- 函数类型,允许函数接受其他函数作为参数,或者返回其他函数。
- lambda 表达式,让你用最少的样板代码方便地传递代码块
- 数据类,提供了创建不可变值对象的简明语法
- 标准库中包括了丰富的 API 集合,让你用函数式编程风格操作对象和集合
Kotlin 允许你使用函数式编程风格但并没有强制你使用它。当你需要的时候,可以使用可变数据,也可以编写带副作用的函数,而且不需要跳过任何多余的步骤。然后,毫无疑问的是,在 Kotlin 中使用基于接口和类层次结构的库就像 Java 一样简单。当编写 Kotlin 代码的时候,可以结合使用面向对象编程和函数式编程风格,并使用最合适的工具来对付亟待解决的问题。
免费并开源
Kotlin 语言(包括编译器、库和所有相关工具)是完全开源的,并且可以自由使用。它采用 Apache 2 许可证 ;其开发过程完全公开在 GitHub (http://github.com/jetbrains/ kotlin) 上,并且欢迎来自社区的贡献。如果你要开发 Kotlin 应用程序,有三种开源 IDE 供你选择 :IntelliJ IDEA Community2 版、Android Studio 以及 Eclipse,它们都完 全支持 Kotlin(当然,IntelliJ IDEA Ultimate 也支持 Kotlin。)
现在你明白了 Kotlin 是什么语言,让我们看看 Kotlin 在具体的实际应用中会给你带来哪些好处。
Kotlin应用
如前所述,Kotlin 使用的两个主要的领域是服务器端和 Android 开发。接下来我们分别看看这两个领域,以及为什么 Kotlin 非常适合它们。
服务器端的 Kotlin
服务器端编程是一个非常大的概念,它包含了所有下列的应用程序类型甚至更多 :
- 返回 HTML 页面给浏览器的 Web 应用程序
- 通过 HTTP 暴露 JSON API 的移动应用后端服务
- 通过 RPC 协议互相通信的微服务
多年以来,开发者一直在构建这些类型的应用,并且积累了大量的框架和技术来帮助他们构建这些应用。这些应用通常并不是孤立地开发或者从零开始的,它们几乎总是对现有的系统进行扩展、改进或者替换,新的代码必须和系统中现有部分进行集成,而这些部分可能很多年之前就写成了。
这种环境下 Kotlin 的一大优势就是它与现有的 Java 代码无缝的互操作性。无论是要编写一个全新的组件还是移植一个现有服务的代码,Kotlin 都毫无压力。不管你需要在 Kotlin 中继承 Java 类,还是以某种方式注解一个类的方法或字段,都不会遇到任何问题。它带来的优点是系统的代码会更紧凑、更可靠、更易于维护。
与此同时,Kotlin 还引入了许多用于开发这类系统的新技术。例如,对构建器模式的支持让你可以使用更简洁的语法来创建任何对象图,同时保留了语言中全套的抽象机制和代码重用工具。
这个特性的一个最简单的用例就是 HTML 生成库,它可以把一个外部模板语言替换成简洁且完全类型安全的解决方案。这里有一个例子 :
可以轻松地把映射到HTML标签的函数和常规的Kotlin语言结构组合起来。你不再需要使用一门独立的模板语言,也不需要学习新的语法,仅仅使用循环就可以生成HTML页面。
另一个能用上Kotlin干净和简洁的DSL的用例是持久化框架。例如,Exposed 框架(https://github.com/jetbrains/exposed)就提供了易读的 DSL,可以完全使用 Kotlin 代码来描述 SQL 数据库的结构并执行查询操作,并且有全面的类型检查。下面这个小例子展示了可行的做法 :
Android 上的 Kotlin
一个典型的移动应用和一个典型的企业应用完全不同。它更小,更少地依赖与现有的代码集成,通常需要快速交付,同时需要保证在大量的设备上能够可靠地运行。这类项目 Kotlin 也能胜任。
Kotlin 的语言特性,加上支持 Android 框架的特殊编译器插件,让 Android 的开发体验变得高效和愉悦。常见的开发任务,比如给控件添加监听器或是把布局元 素绑定到字段,可以用更少的代码完成,有时甚至根本不用写任何代码(编译器会帮你生成)。同样由 Kotlin 团队打造的库 Anko(https://github.com/kotlin/anko)给许多标准 Android API 添加了 Kotlin 友好的适配器,进一步提升了 Android 的开发体验。
下面是 Anko 的一个简单例子,可以品尝到使用 Kotlin 进行 Android 开发的滋味。只要把这段代码放在一个 Activity 中,一个简单的 Android 应用就做好了!
使用Kotlin带来的另一优势就是更好的应用可靠性。如果你有开发Android应用的经验,你一定对“Unfortunately, Process Has Stopped”对话框深恶痛绝。如果你的应用有未处理的异常,这个对话框就会出现,而这种异常一般是NullPointerException(空指针异常)。Kotlin的类型系统通过精确地跟踪null值,大大减轻了空指针异常问题带来的压力。大部分Java中会导致NullPointerException的代码在Kotlin中无法编译成功,以确保这些错误在应用到达用户手中之前得到修正。
同时,由于 Kotlin 完全兼容 Java 6,使用它并不会带来任何新的编译问题。你可以享受所有 Kotlin 的酷炫新特性,而你的用户仍然可以在他们的设备上使用你的应用,即使他们的设备并没有运行最新版本的 Android 系统。
说到性能,Kotlin 也没有带来任何负面影响。Kotlin 编译器生成的代码执行起来和普通的 Java 代码效率一样。Kotlin 使用的运行时(库)体积相当小,所以编译出来的应用程序包体积也不会增加多少。当你使用 lambda 的时候,它们会被许多 Kotlin 标准库函数内联。lambda 的内联确保不会创建新对象,因此应用程序也不必忍受额外的 GC 暂停。