包
源文件可能开始于包的声明
package foo.bar
fun baz() {}
class Goo {}
// ...
源码中所有的内容(类和函数)都被包含在包的声明中。所以,上述例子中函数baz
的全称是foo.bar.baz
,类Goo
的全称是foo.bar.Goo
。
如果包没有具体说明,那么该文件中所有内容都属于没有名称的默认包。
默认导入
每个Kotlin源文件都会默认导入数个包:
- kotlin.*
- kotlin.annotation.*
- kotlin.collections.*
- kotlin.comparisons.* (since 1.1)
- kotlin.io.*
- kotlin.ranges.*
- kotlin.sequences.*
- kotlin.text.*
根据不同的目标平台,再导入一些附加的包:
- JVM:
- java.lang.*
- kotlin.jvm.*
- JS:
- kotlin.js.*
自定义导入
除了默认导入的包,每个文件可能包含他自己输入的指令。
我们既可以导入单个名称
import foo.Bar // 现在可以使用Bar而不需要其他的先决条件
也可以导入范围内所有可以访问的内容
import foo.* // foo 中所有的内容都可以访问
如果存在命名冲突,我们可以使用关键词as
在本地重命名有冲突的实体来消除歧义。
import foo.Bar // Bar可以访问
import bar.Bar as bBar // bBar 表示'bar.Bar'
关键字import
并不局限于导入类,还可以用来导入别的声明
- 顶层函数和属性
- 对象声明中的函数和属性
- 枚举常量
不同于Java,Kotlin不支持单独
import static
句法,所有的这些声明都是用常用的import关键字。
顶层声明的权限
如果顶层声明的被标记成private
,那么它只属于文件内部私有。
控制流
if表达式
在Kotlin中,if是一个表达式,即,if有返回值。因此没有三元运算符(条件?yes:no),因为if已经很好的扮演了这个角色。
// 传统使用方式
var max = a
if (a < b)
max = b
// 使用else时
var max: Int
if (a > b) {
max = a
} else {
max = b
}
// 当做表达式
val max = if (a > b) a else b
if分支可以使用代码块,块中的最后一个表达式就是改代码块的值。
val max = if (a > b) {
print("Choose a")
a
} else {
print("Choose b")
b
}
如果使用if作为表达式而不是语句(如:返回它的值或者赋值给一个变量),那么表达式就需要else分支。
when表达式
when
替换C类编程语言的switch
操作,它具有简单的格式,如下:
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> { // Note the block
print("x is neither 1 nor 2")
}
}
when
用它的参数按顺序匹配所有的分支,直到满足某个分支条件。when
也可以当做表达式或语句使用。如果当做表达式使用,那满足条件的分支就是整个表达式的值。如果当做语句使用,那每个分支的值将会被忽略。同if一样,每个分支可以是一个代码块,每个代码块的值就是该代码块内最后一个表达式的值。
如果多个案例都用相同的处理方式,那么分支条件可以用逗号绑定。
when (x) {
0, 1 -> print("x == 0 or x == 1")
else -> print("otherwise")
}
还可以用任意的表达式当做分支条件,不仅仅是常量。
when (x) {
parseInt(s) -> print("s encodes x")
else -> print("s does not encode x")
}
也可以用in
、!in
检验序列或集合中是否存在某个值
when (x) {
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
!in 10..20 -> print("x is outside the range")
else -> print("none of the above")
}
另一方面,我们可以使用is
、!is
检查值是不是一些特殊的类型。
需要注意的是,由于智能转换的作用,你可以直接访问类型的属性和方法而不需要直接转换。
fun hasPrefix(x: Any) = when(x) {
is String -> x.startsWith("prefix")
else -> false
}
when
还可以替换if-else``if
使用,如果没有提供参数,分支条件便是一个简单的boolean表达式,当表达式值是true时,便会执行分支的代码。
when {
x.isOdd() -> print("x is odd")
x.isEven() -> print("x is even")
else -> print("x is funny")
}
for循环
for循环可以迭代任何提供了迭代器的对象。句法如下:
for (item in collection) print(item)
循环体可以是代码块
for (item: Int in ints) {
// ...
}
照之前所描述,for循环可以迭代任何提供了迭代器的对象,即:该对象有个成员(或扩展)函数iterator(),函数的返回类型有个成员(或扩展)函数next()还有个返回Boolean类型的成员(或扩展)函数hasNext()。、
所有的这三个方法都需要用operator
标记
for寻短迭代数组是编译成基于索引的循环,因此它没有创建迭代器对象。
如果想要根据索引迭代数组或序列,可以按照下述方式:
for (i in array.indices) {
print(array[i])
}
迭代一个序列是在编译时做优化处理,没有创建额外对象。
或者,使用库函数withIndex()
for ((index, value) in array.withIndex()) {
println("the element at $index is $value")
}
while循环
while
和do...while
循环的工作原理同平常一样。
while (x > 0) {
x--
}
do {
val y = retrieveData()
} while (y != null)
循环内的break和continue
在循环内部,kotlin支持传统的break
和continue
操作。
返回和跳转
kotlin有三种跳转结构表达式:
- return 默认情况下,从最近的封闭函数(或匿名函数)返回
- break 中断最近的封闭循环
- continue 处理最近循环中的下一步
所有的这些表达式都可以当做某个更大的表达式的一部分:
val s = x.name ?: return //name==null时,执行return
break和continue标签
Kotlin中任何表达式都可以标记为标签。标签通常有可识别的格式用后缀@标记。如:abc@,fooBar@都是合法的标签。给表达式贴上标签只需在其前面添加一个标签即可。
loop@ for (i in 1..100) {
// ...
}
现在我们可以使用标签执行break
和continue
loop@ for (i in 1..100) {
for (j in 1..100) {
if (...) break@loop
}
}
一个有资格使用标签的break跳转到被标签标记的循环右面的地方开始执行。continue处理循环的下一次迭代。
return到标签
Kotlin中与函数式、本地函数和对象表达式对比,函数可以嵌套。有资格return
的地方允许我们返回到外层函数。最重要的使用案例是从lambda表达式中返回。当我们这样写时会调用。
fun foo() {
ints.forEach {
if (it == 0) return
print(it)
}
}
return
表达式从最近的函数返回,即 foo。注意,这种非本地返回只支持传递给内联函数的lambda表达式。如果我们需要从lambda表达式返回,我们需要先标记才可以。
fun foo() {
ints.forEach lit@ {
if (it == 0) return@lit
print(it)
}
}
现在,它只从lambda表达式返回。通常情况下,使用隐式标签非常方便。这样的标签与lambda传递的函数的名称相同。
fun foo() {
ints.forEach {
if (it == 0) return@forEach
print(it)
}
}
或者,我们把lambda表达式替换成一个匿名函数。一个匿名函数内部的返回语句会从他本身返回。
fun foo() {
ints.forEach(fun(value: Int) {
if (value == 0) return
print(value)
})
}
当我们返回一个值时,解析器会优先选择有资格返回,即:
return@a 1
表示给标签@a返回1,而不是返回一个标签表达式@a 1