最近在Safari Book Online上阅读Big Nerd Ranch Guide的Kotlin Programming一书。此书写得非常精彩,虽然是从最基础的语法开始交起,对于我这个有着十载编程经验的人,依然很有收获。由于Kotlin的知识点非常多,大脑无法在短时间没有实作的巩固下牢记。所以留以此文已备参考。
第一章:Your First Kotlin Application
首先JetBrains出产的IntelliJ IDEA是编写Kotlin程序不二的选择。为什么?
IntelliJ IDEA, called IntelliJ for short, helps you write well-formed Kotlin code. It also streamlines the development process with built-in tools for running, debugging, inspecting, and refactoring your code.
IntelliJ的项目
A project includes all of the source code for your program, along with information about dependencies and configurations. A project can be broken down into one or more modules, which are like subprojects. By default, a new project has one module, which is all you need for your simple first project.
Sandbox
项目的结构
The Sandbox.iml file contains configuration information specific to your single module. The .idea folder contains settings files for the entire project as well as those specific to your interaction with the project in the IDE (for example, which files you have open in the editor). Leave these auto-generated files as they are.
Sandbox.iml
文件是针对单个模块的,包含该模块配置信息。.idea
文件夹包含整个项目的设置和你与项目在IDE中交互设置文件。
The External Libraries entry contains information about libraries the project depends on. If you expand this entry you will see that IntelliJ automatically added Java 1.8 and KotlinJavaRuntime as dependencies for your project.
接下来是教授任何一门新语言的第一个程序:Hello World程序。创建Hello.kt
并键入:
fun main(args: Array) {
println("Hello, world!")
}
注意:IntelliJ提供了一个编写main
函数桩代码的快捷方式,输入单词main
然后按下Tab
键。
相比Java,Kotlin做最简单的事情,也比Java简单很多,不再像“万物皆对象”那样,为一个简单的Hello World程序也要创建一个类,把函数包进类定义中。Kotlin也不再称main为方法(method),而是函数(function)。
This particular function – the main function indicates the starting place for your program. This is called the application entry point, and one such entry point must be defined for any program to be runnable.
println()是Kotlin标准库(Kotlin standard library)的一个内建函数。
Kotlin/JVM代码的编译与执行
按下运行HelloKt
按钮知道控制台输出Hello, World!
之间,幕后发生了很多事。
First, IntelliJ compiles the Kotlin code using the
kotlinc-jvm
compiler. This means IntelliJ translates the Kotlin code you wrote into bytecode, the language the JVM “speaks.” Ifkotlinc-jvm
has any problems translating your Kotlin code, it will display an error message (or messages) giving you a hint about how to fix the issues. Otherwise, if the compilation process goes smoothly, IntelliJ moves on to the execution phase.
In the execution phase, the bytecode that was generated by
kotlinc-jvm
is executed on the JVM. The console displays any output from your program, such as printing the text you specified in your call to the println() function, as the JVM executes the instructions.
When there are no more bytecode instructions to execute, the JVM terminates. IntelliJ shows the termination status in the console, letting you know whether execution finished successfully or with an error code.
The Kotlin REPL
Kotlin REPL的话,目前还很难感知它的实用性,不过记住它的入口是Tools->Kotlin->Kotlin REPL
,还有记住在REPL中evaluate代码的快捷键:Command-Return (Ctrl-Return)
For the More Curious: Why Use IntelliJ?
曾记得十年前初学Java时,我是习惯使用普通的文本编辑器(EditPlus)编写Java示例代码的。Kotlin当然也可以使用text editor编写,但是此书作者建议初学者使用IntelliJ。如此来看,Kotlin沦陷为最好在IDE中编写程序的语言了。确实IntelliJ非常的智能,程序员仰赖IntelliJ对代码进行的细致的检测以写出语法正确的代码。正如该节末尾所指出的:
Also, since Kotlin was created by JetBrains, the integration between IntelliJ and Kotlin is carefully designed – often leading to a delightful editing experience.
For the More Curious: Targeting the JVM
The JVM is a piece of software that knows how to execute a set of instructions, called bytecode. “Targeting the JVM” means compiling, or translating, your Kotlin source code into Java bytecode, with the intention of running that bytecode on the JVM.
Since Kotlin can be converted to bytecode that the JVM can execute, it is considered a JVM language. Java is perhaps the most well-known JVM language, because it was the first. However, other JVM languages, such as Scala and Kotlin, have emerged to address some shortcomings of Java from the developer perspective.
Kotlin is not limited to the JVM, however. At the time of this writing, Kotlin can also be compiled into JavaScript or even into native binaries that run directly on a given platform – such as Windows, Linux, and macOS – negating the need for a virtual machine layer.
第二章:变量、常量和类型(Types)
Types describe the particular kind of data that is held by a constant or variable.
变量和常量有一个指定的数据类型,告诉了编译器如何处理type checking
type checking will be handled, a feature in Kotlin that prevents the assignment of the wrong kind of data to a variable or constant.
声明一个变量
Kotlin使用var
来声明变量,如果编译器能够推断出变量的具体类型,那么类型就可以不用声明。比如:
fun main(args: Array) {
var experiencePoints: Int = 5
println(experiencePoints)
}
也可以简化成:
fun main(args: Array) {
var experiencePoints = 5
println(experiencePoints)
}
似乎IntelliJ并不鼓励在可以推断类型的情况下显示声明类型。
Kotlin uses a static type system – meaning the compiler labels the source code you define with types so that it can ensure the code you wrote is valid. IntelliJ also checks code as you type it and notices when an instance of a particular type is incorrectly assigned to a variable of a different type. This feature is called static type checking, and it tells you about programming mistakes before you even compile the program.
Kotlin's Built-in Types
Kotlin的内建数据类型有String
, Char
, Boolean
, Int
, Double
, List
, Set
, Map
,虽然这些类型在Java中也有,但是要记住这些是Kotlin自己定义的类型。
Read-only Variables只读变量
You declare a variable that can be modified using the var keyword. To declare a read-only variable, you use the val keyword.
var
关键字的引入,就我来看,是起到强促程序员多多使用常量。就像作者所说:
...we recommend that you use a val any time you do not need a var.
IntelliJ can detect when a var can be made a val instead by analyzing your code statically. If a var is never changed, IntelliJ will suggest that you convert it to a val.
To apply the suggestion, click on the var keyword next to playerName and press Option-Return (Alt-Enter). In the pop-up, select Make variable immutable
Type inference
Kotlin includes a feature called type inference that allows you to omit the type definition for variables that are assigned a value when they are declared.
Note that IntelliJ will display the type of any variable on request, including those that use type inference. If you ever have a question about the type of a variable, click on its name and press Control-Shift-P. IntelliJ will display its type.
Compile-Time Constants
val
声明的数据并不一定是不可改变的常量,如果要让数据绝对不可变,要考虑用编译时常量。
A compile-time constant must be defined outside of any function, including main, because its value must be assigned at compile time (that is, when the program compiles) – hence the name. main and your other functions are called during runtime (when the program is executed), and the variables within them are assigned their values then. A compile-time constant exists before any of these assignments take place.
Compile-time constants also must be of one of the basic types(String, Int, Double, Float, Long, Short, Byte, Char, Boolean), because use of more complex types for a constant could jeopardize the compile-time guarantee.
const val MAX_EXPERIENCE: Int = 5000
fun main(args: Array) {
...
}
这里定义的变量并不是local to a function or class – a file-level variable。
Prepending a val with the const modifier tells the compiler that it can be sure that this val will never change. In this case, MAX_EXPERIENCE is guaranteed to have the integer value 5000, no matter what. This gives the compiler the flexibility to perform optimization behind the scenes.
Inspecting Kotlin Bytecode
Kotlin is an alternative to Java for writing programs that run on the JVM, where Java bytecode is executed. It is often useful to inspect the Java bytecode that the Kotlin compiler generates to run on the JVM. You will look at the bytecode in several places in this book as a way to analyze how a particular language feature works on the JVM.
Knowing how to inspect the Java equivalent of the Kotlin code you write is a great technique for understanding how Kotlin works, especially if you have Java experience.
For the More Curious: Java Primitive Types in Kotlin
Java中有Primitive类型和Reference类型。引入Primitive Types是出于性能考量:实例化开销、内存占用都会小很多...;Kotlin仅提供一种数据类型:Reference类型。
Kotlin made this design decision for several reasons. First, if there is no choice between kinds of types, you cannot code yourself into a corner as easily as you can with multiple kinds to choose from. For example, what if you define an instance of a primitive type, then realize later that you need to use the generic feature, which requires a reference type? Having only reference types in Kotlin means that you will never encounter this problem.
Kotlin编译器会将可以转换为Primitive类型的应用类型转换为Primitive类型
var experiencePoints: Int = 5
会被转换成
int experiencePoints = 5;
Kotlin gives you the ease of reference types with the performance of primitives under the hood. In Kotlin you will find a corresponding reference type for the eight primitive types you may be familiar with in Java.
第三章:Conditionals
==
在Kotlin中是结构相等操作符(Structural equality operator),被读作“相等”。
Using the addition operator (
+
) to append a value to a string is called string concatenation. It is an easy way to customize what is printed to the console based on the value of a variable.
+
运算符在字符串上被重载为字符串拼接操作,Kotlin推荐使用String templating
来把值注入到字符串内。
Ranges
虽然Java中也可以写一个自定义的Range类来支持表示“范围”的概念,但是Kotlin是从语言级别上支持这个概念。如果不是从语言级别上支持的话,任何的模仿只是拙劣的模仿。
Kotlin provides ranges to represent a linear series of values.
In addition to the .. operator, several functions exist for creating ranges. The downTo function creates a range that descends rather than ascends, for example. And the until function creates a range that excludes the upper bound of the range specified.
... use the
in
keyword to check whether a value is within a range...
when表达式
窃以为,when
是Kotlin作者对Java中的switch
的改进。
By default, a when expression behaves as though there were a == equality operator between the argument you provide in parentheses and the conditions you specify in the curly braces.
val healthStatus = when (healthPoints) {
100 -> "is in excellent condition!"
in 90..99 -> "has a few scratches."
in 75..89 -> if (isBlessed) {
"has some minor wounds but is healing quite quickly!"
} else {
"has some minor wounds."
}
in 15..74 -> "looks pretty hurt."
else -> "is in awful condition!"
}
String Templates字符串模板
Kotlin features string templates to aid in this common need and, again, make your code more readable. Templates allow you to include the value of a variable inside a string’s quotation marks.
$ symbol indicates to Kotlin that you would like to template a val or var within a string you define, and it is provided as a convenience.
Kotlin also allows you to evaluate an expression within a string and interpolate the result – that is, to insert the result into the string. Any expression that you add within the curly braces after a dollar-sign character (
${}
) will be evaluated as a part of the string.
println("(Aura: $auraColor) (Blessed: ${if (isBlessed) "YES" else "NO"})")
println("$name $healthStatus")
第四章:函数
把重复执行的语句提取并重构成方法是IntelliJ提供的便利:
Control-click (right-click) on the code you selected and choose Refactor → Extract → Function…
Anatomy of a Function
A function can optionally begin with a visibility modifier. The visibility modifier determines which other functions can “see” – and therefore use – the function.
By default, a function’s visibility is public – meaning that all other functions (including functions defined in other files) can use the function. In other words, if you do not specify a modifier for the function, the function is considered public.
For each parameter, the definition also specifies the type of data it requires.
在方法头,参数的定义是必须指定数据类型的,不可以依赖类型推断。
Note that function parameters are always read-only – they do not support reassignment within the function body. In other words, within the body of a function, a function parameter is a val, instead of a var.
The file-level variable can be accessed from anywhere in the project (though a visibility modifier can be added to the declaration to change its visibility level). File-level variables remain initialized until program execution stops.
Because of the differences between local and file-level variables, the compiler enforces different requirements on when they must be assigned an initial value, or initialized.
File-level variables must always be assigned when they are defined, or the code will not compile. This requirement protects you from unexpected – and unwanted – behavior, like a variable not having a value when you try to use it.
Since a local variable is more limited in where it can be used – within the scope of the function in which it is defined – the compiler is more lenient about when it must be initialized. A local variable only has to be initialized before it is used.
Default Arguments默认实参
...you can assign a default value for a parameter that will be assigned if no argument is specified.
private fun castFireball(numFireballs: Int = 2) {
println("A glass of Fireball springs into existence. (x$numFireballs)")
}
castFireball(5)
castFireball()
Single-Expression Functions
For single-expression functions, you can omit the return type, curly braces, and return statement.
private fun castFireball(numFireballs: Int = 2) =
println("A glass of Fireball springs into existence. (x$numFireballs)")
Notice that instead of using the function body to specify the work the function will perform, with single-expression function syntax you use the assignment operator (=), followed by the expression.