Kotlin之基本语法

时间:2022-04-27
本文章向大家介绍Kotlin之基本语法,主要内容包括包、函数、参数、函数调用、中缀符号、Unit类型、单表达式函数、变长参数、函数范围、泛型函数、尾递归函数、字符串、条件表达式、值检查与自动转换、for循环、while 循环、ranges、集合、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

在今年Google IO大会上Google已经明确kotlin作为为Android第一官方语言的地位。我相信Google的决意,就像当初毫不犹豫的抛弃eclipse转向as,kotlin已经是不可避免的大势所趋了。再加上Kotlin与java代码完全兼容,所以从Java转向Kotlin是一件很容易的事情。Kotlin语法简单,它融合了当前多种语言的优势,可以是一门明星语言。

下面就Kotlin的一些基本语法做一个讲解。

一个源文件往往以包声明开始:源文件的所有内容(比如类和函数)都被包声明并包括。

package foo.bar

fun bza() {}

class Goo {}

在上面的例子中, bza() 的全名应该是 foo.bar.bza ,Goo 的全名是 foo.bar.Goo。如果没有指定包名,那这个文件的内容就从属于一个默认的 “default” 包。

Imports

在源文件中,除了模块中默认导入的包,每个文件也可以有它自己的导入指令。比如:

import foo.Bar 

如果不指定特定的文件,那么可以使用*导入范围内的所有可用的内容 (包,类,对象,等等)。比如:

import foo.*   //foo 中的所有内容

如果命名有冲突,我们还可以使用 as 关键字局部重命名解决冲突。

import foo.Bar // Bar 可以使用
import bar.Bar as bBar // bBar 代表 'bar.Bar'

函数

函数声明

在 kotlin 中用关键字 fun 声明函数:

fun double(x: Int): Int {

}

参数

函数参数用 Pascal 符号定义,格式形如:name:type,参数之间用逗号隔开,每个参数必须指明类型。

fun powerOf(number: Int, exponent: Int) {
...
}

函数参数可以设置默认值,默认值可以通过在type类型后使用=号进行赋值,当参数被忽略时会使用默认值,这样做的好处是可以减少重载。

fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size() ) {
...
}

函数调用

在函数内部可以直接使用函数,比如:

val result = double(2) 

如果在其他类需要调用调用成员函数:

Sample().foo() // 创建Sample类的实例,调用foo方法

中缀符号

在满足如下条件时:它们是成员函数或者是扩展函数,只有一个参数 使用infix关键词进行标记。函数可以通过中缀符号进行调用。比如:

//给 Int 定义一个扩展方法
infix fun Int.shl(x: Int): Int {
...
}

1 shl 2 //用中缀注解调用扩展函数

1.shl(2)

Unit类型

如果函数不会返回任何有用值,那么他的返回类型就是 Unit 。Unit 是一个只有唯一值Unit的类型,这个值并不需要被直接返回,相当于Java的Void。

fun printHello(name: String?): Unit {
    if (name != null)
        println("Hello ${name}")
    else
        println("Hi there!")
    // `return Unit` or `return` is optional
}

Unit 返回值也可以省略,例如下面这样:

fun printHello(name: String?) {
    ...
}

单表达式函数

当函数只返回单个表达式时,大括号可以省略,并在 = 后面定义函数体:

fun double(x: Int): Int = x*2

如果进一步精简,还可以写成如下的方式。

fun double(x: Int) = x * 2

变长参数

函数的参数(通常是最后一个参数)可以用 vararg 修饰符进行标记,标记后,允许给函数传递可变长度的参数:

fun asList<T>(vararg ts: T): List<T> {
    val result = ArrayList<T>()
    for (t in ts)
        result.add(t)
    return result
}


val list = asList(1, 2, 3)

可变函数只有一个参数可以被标注为 vararg 。加入vararg并不是列表中的最后一个参数,那么后面的参数需要通过命名参数语法进行传值,再或者如果这个参数是函数类型,就需要通过lambda法则。

当调用变长参数的函数时,我们可以一个一个的传递参数,比如:

asList(1, 2, 3)

或者我们要传递一个 array 的内容给函数,那么就可以使用 * 前缀操作符:

val a = array(1, 2, 3)
val list = asList(-1, 0, *a, 4)

函数范围

Kotlin 中可以在文件顶级声明函数,这就意味者你不用像在Java,C#或是Scala一样创建一个类来持有函数。除了顶级函数,Kotlin 函数可以声明为局部的,作为成员函数或扩展函数。

Kotlin 支持局部函数,比如在一个函数包含另一函数。

fun dfs(graph: Graph) {
  fun dfs(current: Vertex, visited: Set<Vertex>) {
    if (!visited.add(current)) return
    for (v in current.neighbors)
      dfs(v, visited)
  }

  dfs(graph.vertices[0], HashSet())
}

当然,局部函数可以访问外部函数的局部变量(比如闭包)。

fun dfs(graph: Graph) {
    val visited = HashSet<Vertex>()
    fun dfs(current: Vertex) {
        if (!visited.add(current)) return 
        for (v in current.neighbors)
            dfs(v)
    }
    dfs(graph.vertices[0])
}

局部函数甚至可以返回到外部函数。

fun reachable(from: Vertex, to: Vertex): Boolean {
    val visited = HashSet<Vertex>()
    fun dfs(current: Vertex) {
        if (current == to) return@reachable true
        if (!visited.add(current)) return
        for (v  in current.neighbors)
            dfs(v)
    }
    dfs(from)
    return false
}

泛型函数

Kotlin和Java一样,还支持泛型函数。

fun sigletonArray<T>(item: T): Array<T> {
    return Array<T>(1, {item})
}

尾递归函数

Kotlin 支持函数式编程的尾递归。这个允许一些算法可以通过循环而不是递归解决问题,从而避免了栈溢出。当函数被标记为 tailrec 时,编译器会优化递归,并用高效迅速的循环代替它。

tailrec fun findFixPoint(x: Double = 1.0): Double 
    = if (x == Math.cos(x)) x else findFixPoint(Math.cos(x))

这段代码计算的是数学上的余弦不动点。Math.cos 从 1.0 开始不断重复,直到值不变为止,结果是 0.7390851332151607。上面的代码等效于:

private fun findFixPoint(): Double {
    var x = 1.0
    while (true) {
        val y = Math.cos(x)
        if ( x == y ) return y
        x = y
    }
}

注:使用 tailrec 修饰符必须在最后一个操作中调用自己。在递归调用代码后面是不允许有其它代码的,并且也不可以在 try/catch/finall 块中进行使用。当前的尾递归只在 JVM 的后端中可以用。

字符串

fun main(args: Array<String>) {
  var a = 1
  // 使用变量名作为模板:
  val s1 = "a is $a"

  a = 2
  // 使用表达式作为模板:
  val s2 = "${s1.replace("is", "was")}, but now is $a"
  println(s2)
}

条件表达式

fun maxOf(a: Int, b: Int): Int {
    if (a > b) {
        return a
    } else {
        return b
    }
}

fun main(args: Array<String>) {
    println("max of 0 and 42 is ${maxOf(0, 42)}")
}

上面的表达式可简写为:

fun maxOf(a: Int, b: Int) = if (a > b) a else b

fun main(args: Array<String>) {
    println("max of 0 and 42 is ${maxOf(0, 42)}")
}

值检查与自动转换

使用 is 操作符检查一个表达式是否是某个类型的实例。如果对不可变的局部变量或属性进行过了类型检查,就没有必要明确转换:

fun getStringLength(obj: Any): Int? {
  if (obj is String) {
    // obj 将会在这个分支中自动转换为 String 类型
    return obj.length
  }

  // obj 在种类检查外仍然是 Any 类型
  return null
}


fun main(args: Array<String>) {
  fun printLength(obj: Any) {
    println("'$obj' string length is ${getStringLength(obj) ?: "... err, not a string"} ")
  }
  printLength("Incomprehensibilities")
  printLength(1000)
  printLength(listOf(Any()))
}

for循环

Kotlin简化了for循环的方式,例如:

fun main(args: Array<String>) {
  val items = listOf("apple", "banana", "kiwi")
  for (item in items) {
    println(item)
  }
  //或者使用下面的方式
  //for (index in items.indices) {
  // println("item at $index is ${items[index]}")
  //}
}

while 循环

fun main(args: Array<String>) {
  val items = listOf("apple", "banana", "kiwi")
  var index = 0
  while (index < items.size) {
    println("item at $index is ${items[index]}")
    index++
  }
}

ranges

检查 in 操作符检查数值是否在某个范围内:

fun main(args: Array<String>) {
  val x = 10
  val y = 9
  if (x in 1..y+1) {
      println("fits in range")
  }
}

检查数值是否在范围外:

fun main(args: Array<String>) {
  val list = listOf("a", "b", "c")

  if (-1 !in 0..list.lastIndex) {
    println("-1 is out of range")
  }
  if (list.size !in list.indices) {
    println("list size is out of valid list indices range too")
  }
}

在范围内迭代:

fun main(args: Array<String>) {
  for (x in 1..5) {
    print(x)
  }
}

或者使用步进:

fun main(args: Array<String>) {
  for (x in 1..10 step 2) {
    print(x)
  }
  for (x in 9 downTo 0 step 3) {
    print(x)
  }
}

集合

对一个集合进行迭代:

fun main(args: Array<String>) {
  val items = listOf("apple", "banana", "kiwi")
  for (item in items) {
    println(item)
  }
}

使用 in 操作符检查集合中是否包含某个对象。

fun main(args: Array<String>) {
  val items = setOf("apple", "banana", "kiwi")
  when {
    "orange" in items -> println("juicy")
    "apple" in items -> println("apple is fine too")
  }
}

使用lambda表达式过滤和映射集合:

fun main(args: Array<String>) {
  val fruits = listOf("banana", "avocado", "apple", "kiwi")
  fruits
    .filter { it.startsWith("a") }
    .sortedBy { it }
    .map { it.toUpperCase() }
    .forEach { println(it) }
}

附:高阶函数和lambda表达式