变量
声明变量有两种方式 val 和 var,前者声明不可变变量,后者声明可变变量,Kotlin认为变量应首先考虑声明为 val,val 变量所指向的对象可变,这点与大部分高级编程语言类似。Kotlin 有优秀的类型推断机制,我们声明变量时应指定变量的值或变量的数据类型。
值得注意的是,声明数据类型的变量在使用前要被赋值,该检查属于编译期行为。我们如果使用 lateinit 关键字(仅可)声明可变变量则会委托到运行时进行,表示该变量在编译期无法证明是否初始化。常用于依赖注入框架等场景;成员变量不允许 1、2 形式的赋值方式,换句话说成员变量不允许在对象生成前尚未完成初始化,所以可以声明类型变量后在 init 块进行初始化。
kotlin
val a = 1
1. val b: Int
b = 2
2. var c = 3
c = 4
3.
class cla {
val d: Int
init {
d = 5
}
}
类型转换
Kotlin 的基本数据类型均有类包装,所以字面量可以调用函数。Kotlin 不允许隐式的类型转换(1),但允许字面量到目标类型的转换(3),显式进行类型转换的方式是使用 to 函数。
kotlin
var a: Int = 1
1. var b: Long = a /* x */
2. var b: Long = a.toLong() /* √ */
3. var b: Long = 1.toLong() /* √ */
语句
Kotlin 的 while 循环语句和 Java 无异,for 循环语句使用 for-in 形式,访问集合的 in 前方变量会赋值成各集合元素,.. 、until 和 downTo 均能够生成区间,.. 生成的区间是全闭区间,until 生成的区间是左开右闭区间,downTo 生成的区间是倒序全闭区间,步长指区间每次迭代跳过的索引数,默认步长为 1,可以在表达式尾用 step 指定。
kotlin
while (expression) { ... }
val arr = IntArray(5) /* 创建长度为5的Int数组 */
1. for (num in arr) { ... }
2. for (i in 0..arr.size - 1) { ... }
3. for (i in 0..arr.size - 1 step 2) { ... }
4. for (i in 0 until arr.size) { ... }
5. for (i in arr.size - 1 downTo 0) { ... }
if 语句和对标 Java Switch 的 When 语句都是表达式,能够返回符合条件代码块的最后 1 行表达式的值,使用方式是
kotlin
fun max(a: Int, b: Int): Int = if (a > b) a else b
fun is10(c: Int): Boolean = when (c) {
10 -> true
else -> fal
}
数组
Kotlin 有两种数组 Array 和 IntArray,分别对应 Java 层 Integer[] 和 int[] 两种数组,相互转换的底层逻辑是拷贝,分别会调用 toTypedArray 和 toIntArray 函数
kotlin
val a = arrayOf(1, 2, 3)
val b = IntArray(5)
val c: Array<Int> = c.toTypedArray()
val d: IntArray = a.toIntArray()
函数
顶层函数
顶层函数是 Kotlin 针对不应从属于某个类的函数的设计,也就是工具类和入口类,我们能在 IDEA 的示例代码注意的,main 函数就是一个顶层函数,在 Java 层会将这些函数包裹在自动生成类中作为静态方法出现。
kotlin
fun main() {
val name = "Kotlin"
println("Hello, " + name + "!")
for (i in 1..5) {
println("i = $i")
}
}
顶层函数以 kt 文件为单位独立存在,通常自动生成类的类名为 kt 文件名,也可以通过注解自定义指定,该注解会默认为整个文件的所有顶层函数生成该类,且通常在不同文件声明相同的自动生成类名会致使命名冲突错误。
kotlin
@file:JvmName("MathUtils")
fun add(a: Int, b: Int) = a + b
/* -> */
public final class MathUtils {
/* 没有构造函数 */
public static int add(int a, int b) {
return a + b;
}
}
顶层属性的概念和顶层函数类似,也会在自动生成类定义 private static 字段,同时会生成字段的 get 和 set 方法(如果属性为 val 则没有 set 方法)。顶层属性会在文件对应类第一次被加载时执行初始化操作。此外,如果希望以 public static final 形式将属性暴露给 Java,则可以使用 const 修饰所有的基本数据类型和 String,且该属性必须是 val 且要被适当初始化。
kotlin
/* God.kt */
val name = "洪秀全"
const val rank = 2
/* -> */
public final class GodKt {
private static final String name = "洪秀全";
public static getName() {
return name;
}
public static final int rank = 2;
}
扩展函数
Kotlin 的 API 相较于 Java 更加丰富,这不是通过魔改 Java API 来实现的,而是使用扩展函数,它就是某个类的成员函数,但是定义在该类的外面。扩展函数和顶层函数在 Kotlin 的地位等同,扩展函数在 Java 层也是静态方法,但扩展函数可以分为顶层扩展函数和成员扩展函数,二者的上下文范围不同,后者更局限一些,只能在所在类实例的上下文环境调用。
kotlin
/* StringUtils.kt */
fun String.lastChar(): Char = this.get(this.length - 1)
/* -> */
public final class StringUtilsKt {
public static char lastChar(String s) {
return s.charAt(s.length() - 1);
}
}
/* --- */
class SomeClass {
fun String.lastChar(): Char = this.get(this.length - 1)
}
/* -> */
public final class SomeClass {
public static char lastChar(SomeClass sc, String s) {
return s.charAt(s.length() - 1);
}
}
在顶层函数和扩展函数的介绍中不难注意到的点是,Kotlin 文件也有工程意义,区别于 Java 文件名与 public 顶级类类名对应的情况,Kotlin 文件可以包含多个非文件名的类和多个顶层函数或属性。
扩展属性可以视为以变量角度获得扩展函数值的方法,因为我们并没有直接修改扩展类的代码,所以扩展属性字段本身是不存在的,不能进行初始化,也没有默认 get 实现,要自定义 get 函数。
kotlin
val String.lastChar: Char
get() = get(length - 1)
/* -> */
public final class SomeClass {
public static char getLastChar(String s) {
return s.charAt(s.length() - 1);
}
}
可变参数与中缀调用
Kotlin 可以用 mapOf 函数创建哈希表,类似 Java 的 asList,该函数传递的参数是可变的,可变性是通过 vararg 关键字修饰形式参数得到的,值得注意的是,Java 的可变函数可以传入数组,但 Kotlin 要求开发者显式解包数组,解包的方式是使用展开运算符 * 修饰对应参数。
使用 mapOf 构建哈希表会注意到, to 表示 key 到 value 的映射结构,这是特殊的函数调用,即中缀调用。一般 to 函数的调用形式如 3,返回 Pair<Int, String> 实例,中缀函数可以与唯一参数的函数共同使用,通过 infix 修饰符以声明中缀函数,to 函数是扩展函数,可以创建任意元素对,作为泛型接收者的扩展,其函数声明如 4。
kotlin
/* mapOf的函数声明 */
1. public fun <K, V> mapOf(vararg pairs: Pair<K, V>): Map<K, V>
2. val map = mapOf(1 to "one", 2 to "two", 3 to "three")
3. 1.to("one")
4. infix fun Any.to(other: Any) = Pair(this, other)
5.
fun main(args: Array<String>) {
val list = listOf("args: ", *args)
/* 相当于用"args: "和args的各参数建立集合 */
}