Kotlin基础语法

定义函数

带有两个Int参数、返回Int的函数:

fun sum(a: Int, b: Int): Int{
    return a+b
}

//上面函数等效于
//将表达式作为函数体、返回值类型自动推断
fun sum(a: Int, b: Int) = a+b

fun main(args: Array<String>) {
    print("sum of 3 and 5 is ")
    println(sum(3,5))
}

fun main(args: Array<String>){
    println("sum of 3 and 5 is ${sum(3,5)}")
}

其他含参函数类似用法

函数返回无意义的值:

fun sum(a: Int): Unit{
    println("函数返回无意义的值")
}
//Unit 返回类型可以省略
fun sum(a: Int){
    println("函数返回无意义的值")
}

定义变量

定义局部变量

一次赋值(只读)的局部变量:

fun main(args: Array<String>){
    val a: Int = 1 //立即赋值
    val b = 2   //自动推断出Int类型
    val c: Int  //如果没有初始值,类型不能省略
    c = 3      //明确赋值
}

可变变量:

fun main(args: Array<String>){
    var a: Int = 1 //立即赋值
    var b = 2   //自动推断出Int类型
    var c: Int  //如果没有初始值,类型不能省略
    c = 3      //明确赋值
}

注释

和Java类似,与Java不同的是,Kotlin的块注释可以嵌套

使用字符串模板

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)
}

使用条件表达式

使用可空值及null检测

当某个变量的值可以为null的时候,必须在声明处的类型后添加 ? 来标识该引用可以为空。

如果 str 的内容不是数字返回null:

fun parseInt(str: String): Int?{
    ...
}
使用返回可空值的函数:
fun parseInt(str: String): Int?{
    return str.toIntOrNull()
}

fun printProduct(arg1: String,arg2: String){
    val x = parseInt(agr1)
    val y = parseInt(arg2)
    
    //直接使用 x*y 可能会报错,因为他们可能为null
    if (x != null && y != null){
        //在空检测后,x和y会自动转换为非空值(non-nullable)
        println(x*y)
    }else{
        println("x or y is null")
    }
    
    //或者可以以下形式判空
    //sampleStart
    if (x == null) {
        return
    }
    if (y == null) {
        return
    }
    println(x*y)
    //sampleEnd
}

使用类型检测及自动类型转换

is 运算符检测一个表达式是否是某类型的一个实例。如果一个不可变的局部变量或属性已经判断出为某类型,那么检测后的分支中可以直接当做改类型使用,无需显示转换:

fun getStringLength(obj: Any): Int?{
    if (obj is String){
    // obj 在该条件分支内自动转换成 String
        return obj.length
    }
    
    //在离开类型检测分之后, obj 仍然是 Any 类型
    return null
}
//或者以下比较
fun getStringLength(obj: Any): Int?{
    if (obj !is String) return null
    
    // obj 在该条件分支内自动转换成 String
    return obj.length
}

使用区间(range)

使用 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 1..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) {
        println(x)
    }
}

数列迭代

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

基本类型

数字

Kotlin处理数字在某种成都上接近Java,但并不完全相同。例如:对于数字没有隐式拓宽转换(如Java中 int 可以隐式转换为 long)

Kotlin提供了如下的内置类型来表示数字(与Java很相近)

Type Bit
Double 64
Float 32
Long 64
Int 32
Short 16
Byte 8

字面常量

数值常量字面值有以下几种:

  • 十进制:123(Long类型用大写 L 标记:123L)
  • 十六进制:0x0F
  • 二进制:0b0110
注意:不支持八进制
Kotlin同样支持浮点数的常规表示方法:
  • 默认double:123.5
  • Float用 f 或者 F 标记

数字字面值中的下划线

可以使用下划线使数字常量更易读:

val oneMillion = 1_000_000

表示方式

在Java平台数字是物理存储为JVM的原生类型,除非我们需要一个可空的引用(如:Int?)或泛型。后者情况下会把数字装箱。

注意数字装箱不必保留同一性
val a: Int = 10000
pritn(a === a)//输出“true”
val boxedA: Int? = a
val anotherBoxedA: Int? = a
print(boxedA === anotherBoxedA)//输出 false
另一方面,它保留了相等性:
val a: Int = 10000
pritn(a == a)//输出“true”
val boxedA: Int? = a
val anotherBoxedA: Int? = a
print(boxedA == anotherBoxedA)//输出 true

显示转换

由于不同的表示方式,较小类型并不是较大类型的子类型。因此较小的类型不能隐式转换为较大的类型。这意味着在不进行显示转换的情况下我们不能把Byte类型赋值给一个Int变量。
可以显示转换来拓宽数字
  • toByte()
  • toShort()
  • toInt()
  • toLong()
  • toFloat()
  • toDouble()
  • toChar()
缺乏隐式类型转换并不显著,因为类型会从上下文推断出来,而算术运算会有重载做适当转换,例如:

val l = 1L + 3

运算

Kotlin支持数字运算的标准集,运算被定义为相应的类成员(但编译器会将函数调用优化为相应的指令)

对于位运算,没有特殊字符来表示,只可用中缀方式调用命名函数,例如:

val x = (1 shl 2) and 0x000FF000

完整的位运算列表(只用于Int和Long):

  • shl(bits) : 有符号左移(Java的 <<)
  • shr(bits) : 有符号右移(Java的 >>)
  • ushr(bits): 无符号右移(Java的 >>>)
  • and(bits) : 位与
  • or(bits) : 位或
  • xor(bits) : 位异或
  • inv(bits) :位非

字符

字符用 char 类型表示。他们不能直接当做数字。
字符字面值用单引号括起来: '1'。特殊字符可以用反斜杠转义。支持这几个转义序列:\t,\b,\n,\r,$,',",\。编码其他字符要用Unicode转义序列语法:'\uFF00。
我们可以显示把字符转换为Int数字:

fun decimalDigitValue(c: Char): Int{
    if(c !in '0'..'9'){
        throw IllegaArgumentException("out of range")
    }
    return c.toInt()-'0'.toInt()//显示转换为数字
}

当需要可空引用是,像数字、字符会被装箱。装箱操作不会保留同一性。

布尔

布尔用 Boolean 类型表示,只有两个值: true 和 false。
若需要可空引用布尔会被装箱。
内置的布尔运算有:

  • || 短路逻辑或
  • && 短路逻辑与
  • ! 逻辑非

数组

使用 Array 类来表示,它定义了get和set函数(按照运算符重载约定这回转变为 [])和 size 属性,以及一些其他有用的成员函数:

class Array<T> private constructor(){
    val size: Int
    operator fun get(index: Int): T
    operator fun set(index: Int,value: T): Unit
    operator fun iterator(): Iterator<T>
}

可以使用库函数 arrayOf() 来创建一个数组并传递元素值给它,例如:

arrayOf(1,2,3)

或者可以用库函数 arrayOfNulls() 来创建一个指定大小,元素都为空的数组。
另一个选择是用接受数组大小和一个函数参数的工厂函数,用作参数的函数能够返回给定索引的每个元素初始值。

//创建一个Array<String> 初始化为["0","1","4","9","16"]
val asc = Array(5, { i -> (i*i).toString })
注意:与Java不同的是,Kotlin中数组是不型变的(invariant)。这意味着Kotlin不让把Array<String>赋值给Array<Any>,以防止可能的运行时失败(但是可以使用Array<outAny>)。

Kotlin也有无装箱开销的专门的类来表示原生类型数组:ByteArray、ShortArray、InArray等等。这些类和Array并没有继承关系,但是它们有同样的方法属性集、它们也都有相应的工厂方法:

val x: IntArray = intArrayOf(1,2,3)
x[0] = x[1] + x[2]

字符串

字符串用 String 类型表示。字符串是不可变的。字符串的元素--字符可以使用索引运算符访问:s[i] 。可以用 for 循环迭代字符串:

for(c in str){
    println(c)
}

字符串字面值

Kotlin有两种类型的字符串字面值:转义字符串可以有转义字符,以及原生字符串可以包含换行和任意文本。

转义字符串

val s = "Hello!\n"

原生字符串:使用三个引号(""")分界符括起来,内部没有转义并且可以包含换行和任何其他字符:
val text = """ 
    我:你好啊!
    你:你好啊!
"""

可以通过 trimMargin() 函数去除前导空格:

val text = """ 
    |我:你好啊!
    |你:你好啊!
""".trimMargin()
默认 | 用作边界前缀,但可以选择其他字符并作为参数传入,比如: trimMargin(">")

字符串模板

字符串可以包含模板表达式,即一些小段代码,会求值并把结果合并到字符串中。模板表达式以($)开头:

val i = 10
val s = "i = $i"//结果为:"i = 10"

或者用花括号括起来:

val s = "abc"
val str = "$s.length is ${s.length}"//结果为:"abc.length is 3"

原生字符串和转义字符串内部都支持模板。如果需要在原生字符串中表示字面值 $ 字符(它不支持反斜杠转义),可以使用如下语法:

val price = """
    ${'$'}9.99
"""

定义包

包的申明应处于源文件顶部

package my.demo

源文件所有内容都包含在声明的包内。如果没有指明包,该文件的内容属于无名字的默认包。

导入

  • 可以导入一个单独的名字
  • 可以导入一个作用域下的所有内容(包、类对象等)
  • 如果出现名字冲突,可以使用 as 关键字在本地重命名冲突来消歧义
  • 关键字import并不仅限于导入包;也可以导入其他声明:
  1. 顶层函数及属性
  2. 在对象声明中声明的函数和属性
  3. 枚举常量
与Java不同,Kotlin没有单独的“import static”语法:所有这些声明都用import关键字导入

顶层声明的可见性

如果顶层声明是 private ,它声明的文件是私有的

控制流程

if表达式

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

//max()等效于
fun max(a: Int,b: Int) = if (a > b) a else b

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

when表达式

fun describe(obj: Any): String = 
    when (obj) {
        1          -> "one"
        "Hello"    -> "World"
        is Long    -> "Long"
        !is String -> "Not a string"
        else       -> "other"
    }

fun main(args: Array<String>){
    println(describe(1))
    println(describe("Hello"))
    println(describe(1000L))
    println(describe(2))
    println(describe("other"))
}

如果很多分支需要用相同的方式处理,可以吧多个分支条件放在一起,用逗号分隔:

when(x){
    0,1 -> print("")
    else -> print("")
}

也可以用任意表达式(而不只是常量)作为分支条件

when(x){
    parseInt(s) -> print("")
    else -> print("")
}

也可以检测一个值在(in)或者不在(!in)一个区间或者集合中:

when(x){
    in 1..10 -> print("")
    !in 10..20 -> print("")
    else -> print("")
}

还可以检测一个值是(is)或者不是(!is)一个特定类型的值.注意:由于智能转换,访问该类型的方法和属性无需任何额外的检测。

fun hasPrefix(x: Any) = when(x) {
    is String -> x.startsWith("prefix")
    else -> false
}

when 也可以用来取代 if - else - if 链。如果不提供参数,所有的分支条件都是简单的布尔表达式:

when{
    x.isOdd -> println("")
    else -> println("")
}

for循环

fun main(args: Array<String>){
    val items = listOf("kotlin","java","js")
    for (item in items){
        println(item)
    }
    
    //或者
    for (index in items.indices){
        println("item at $index is ${items[index]}")
    }
}

可以用库函数 withIndex 来遍历数组或者list

for ((index,value) in array.withIndex()) {
    println("the element at $index is $value")
}

while循环

fun main(args: Array<String>){
    val items = listOf("kotlin","java","js")
    var index = 0
    while (index < items.size){
        println("item at $index is ${item[index]}")
        index++
    }
    
    //do while循环
    do {
        val y = y++
    }while(y != 8)
}

循环中的break和continue

kotlin有三种结构化跳转表达式:

  • return 默认从最直接包围它的函数或者匿名函数返回
  • break 终止最直接包围它的循环
  • continue 继续下一次最直接包围它的循环

break和continue

在Kotlin中任何表达式都可以用标签来标记。标签的格式为标识符后跟 @ 符号:

loop@ for (i in 1..100) {
    for (j in 1..100) {
        if() break@loop
    }
}

标签处返回

fun foo() {
    ints.forEach {
        if (it == 0) return
        print(it)
    }
}

这个return表达式从最直接包围它的函数即 foo 中返回。如果需要从lambda表达式中返回,必须加上标签并用以限制 return:

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)
    }
}
当需要一个返回值时,@tag 1 从标签tag返回1
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容