变量的声明格式:“关键字 变量名称 [:变量类型]”
声明变量有两个关键字:
默认情况下应该尽可能地使用 val 关键字来声明变量,仅在必要的时候使用 var
//声明变量 a,类型为 Int val a: Int = 1 //编译器会分析初始化表达式的值,并把它的类型作为变量的类型 val b = 2
用关键字 fun
声明一个函数:
fun main(args:Array<String>) { println("Hello Kotlin!") }
声明一个有返回值的函数:
//函数的返回值类型为 Int fun sum(a:Int, b:Int): Int { return a + b }
上面这个例子的函数体是由单个表达式( a + b
)构成的,可以把这个表达式作为完整的函数体,这种叫做 表达式函数体
:
fun sum(a:Int, b:Int): Int = a + b
Kotlin 中还有个 类型推导 的概念,上面的例子还可以简化成这样:
//省略函数的返回值类型,Kotlin 会自动推导出返回值类型为 Int //a 和 b 的类型不能省略 fun sum(a:Int, b:Int) = a + b
可以在字符串字面值中引用局部变量,只需要在变量名称前面加上字符 $
:
fun main(args:Array<String>) { val name = "Kotlin" println("Hello$name!") } // 输出:Hello Kotlin!
$
还可以引用更复杂的表达式,只需要把表达式用 ${}
包起来:
fun main(args:Array<String>) { println("Hello${args[0]}") }
用关键字 class
声明一个类:
//name、age、isAdult 都是 Person 的属性,name 和 age 类似于 Java 中构造函数的两个参数。 class Person(val name: String, var age: Int) { val isAdult: Boolean = false }
声明为 val 的属性是只读的,而 var 的属性是可变的(可读写),只读属性会默认生成对应的 getter 访问器(即 getName()
),可变属性会默认生成对应的 getter 和 setter 访问器(即 getAge()
和 setAge(age: Int)
)
根据上面定义的 Person,来看看如何使用:
fun main(args:Array<String>) { //创建一个类的对象时不需要使用 new val person = Person("ljuns", "20") //直接访问属性,实际调用的是 getter println(person.name) //打印结果:ljuns println(person.age) //打印结果:20 //修改属性,实际调用的是 setter person.age = 18 println(person.age) //打印结果:18 println(person.isAdult) //打印结果:false }
上面例子用的都是默认生成的访问器,其实也可以自定义访问器的实现:
class Person(val name: String, var age: Int) { //var 声明的属性需要初始化 var address: String = "" get() { return "abc" } set(value) { //此处的 field 暂时理解为当前属性本身 field = value + "123" } } /** * println(person.address) 打印结果:abc * person.address = "aaa" * println(person.address) 打印结果:aaa123 */
用两个关键字 enum class
声明枚举:
//enum 是一个软关键字,必须出现在 class 前面才有特殊意义,否则就是普通的名称 enum class Color{ RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET }
Kotlin 的枚举是可以声明属性的:
//声明了三个属性:r、g、b enum class Color(val r: Int, val g: Int, val b: Int) { //声明了属性后,定义的常量需要指定属性值 RED(255, 0, 0), ORANGE(255, 165, 0), YELLOW(255, 255, 0), GREEN(0, 255, 0), BLUE(0, 0, 255), INDIGO(75, 0, 130), VIOLET(238, 130, 238); //此处用分号(;)分割枚举常量列表和方法定义,如果没有定义方法可以省略分号 fun rgb() = (r * 256 + g) * 256 + b } //println(Color.BLUE.rgb()) 打印结果:255
上面这个例子的打印语句: println(Color.BLUE.rgb())
等同于:
val color = Color.BLUE println(color.rgb())
when
是一个有返回值的表达式,类似于 Java 的 switch,但是比 switch 功能更加强大, when
允许使用任何对象。
fun getMnemonic(color:Color) = when (color) { //不需要写 break,一个分支中可以用逗号(,)隔开多个值 Color.RED, Color.ORANGE, Color.YELLOW -> "warm" Color.GREEN -> "neutral" Color.BLUE, Color.INDIGO, Color.VIOLET -> "cold" //else 类似于 Java 中的 default else -> throw Exception("Dirty color") } //println(getMnemonic(Color.BLUE)) 打印结果:cold
when
中也可以不使用参数:
fun mixOptimized(color:Color) = when { color == Color.RED -> "Red" else -> throw Exception("Dirty color") }
如果 when
表达式的分支包含了所有的可能性,那么可以省略 else 分支,否则必须要有 else 分支。
Kotlin 使用 is
检查来判断一个变量是否是某种类型,通过检查后可以把变量当作该类型来使用,这种行为称为 智能转换
。此外, as
表示显示转换为特定类型。
interface Expr class Num(val value: Int) : Expr class Sum(val left: Expr, val right: Expr) : Expr fun eval(e:Expr): Int { //使用 is 检查来判断 e 是否是 Num 类型 if(e is Num) { //使用 as 把 e 显示转换为 Num 类型 //此处可以省略,因为经过 is 的检查后可以把 e 当作是 Num 来使用 val n = e as Num return n.value } }
此外, is
也可以用于 when
表达式的分支中:
fun eval(e:Expr): Int = when(e) { is Num -> e.value is Sum -> e.right else -> throw IllegalArgumentException("Unknown expression") }
Kotlin 也有 while 循环和 do-while 循环,语法和 Java 中对应的循环没有什么区别。
区间本质上就是两个值之间的间隔,这两个值通常是数字:一个起始值,一个结束值。如果可以迭代区间中的所有值,这样的区间称为数列。
用 ..
运算符来表示区间,下面是一个 [1, 10] 的区间:
//区间是闭包的 val oneToTen = 1..10
用 until
运算符来表示半闭区间,比如 [1, 10):
val oneToTen = 1 until 10
使用 in
运算符来检查一个值是否在区间中,或者它的逆运算 !in
:
// .. 运算符也可以创建字符区间 fun isLetter(c:Char) = c in 'a'..'z' || c in 'A'..'Z' fun isNotDigit(c:Char) = c !in '0'..'9' //println(isLetter('q')) 打印结果:true //println(isNotDigit('x')) 打印结果:true
同样, in
和 !in
也适用于 when
表达式:
fun recognize(c:Char) = when(c) { in '0'..'9' -> "It`s a digit!" in 'a'..'z', in 'A'..'Z' -> "It`s a letter!" else -> "I don`t know..." } //println(recognize('8')) 打印结果:It`s a digit!
通过小例子来学习迭代 map:
fun main(args:Array<String>) { //创建 TreeMap val binaryReps = TreeMap<Char, String>() //遍历 [A, F] for(c in 'A'..'F') { val binary = Integer.toBinaryString(c.toInt()) binaryReps[c] = binary //类似于 Java中 map.put(c, binary) } //迭代(遍历)。定义两个属性 letter、binary 表示 map 的 key 和value for ((letter, binary) in binaryReps) { println("$letter=$binary") } }