语法总结"/>
Kotiln基础语法总结
kotlin不仅支持编写在虚拟机上运行,而且还是一门跨平台的通用型语言,我们可以用Kotlin开发各种类型的原生应用,如Android,macOS,Windows,Javascript应用。
Kotlin能脱离虚拟机层,直接编译成可以在Windows,Linux和macOS平台上运行的原生二进制代码。
一、变量
变量声明
//Int类型可以省略,自动推导
var max: Int = 5
//var 可读可写
//val 可读不可写
常用内置数据类型
类型 | 描述 | 示例 |
---|---|---|
String | 字符串 | ”Hello World“ |
Char | 单字符 | ‘A’ |
Boolean | true/false | true false |
Int | 整数 | 5 |
Double | 小数 | 3.14 |
List | 元素集合 | |
Set | 无重复元素的集合 | |
Map | 键值对集合 |
二、编译时常量
编译时常量只能在函数之外定义,因为编译时常量必须在编译时赋值,而函数都是在运行时才调用,函数内的变量也是在运行时赋值,编译时常量要在这些变量赋值前就已存在
编译时常量只能时常见的基本数据类型:String、Int、Double、Float、Long、Short、Byte、Char、Boolean
const val MAX = 100
三、查看Kotlin字节码
两种方式
1.Shift键两次,输入Show Kotlin
2.Tools->Kotlin->Show Kotlin Bytecode
Decompile可以反编译成java代码
四、Kotlin的引用类似与基本数据类型
Java有两种数据类型:引用类似与基本数据类型
Kotlin只提供引用类型这一种数据类型
,出于更高性能的需要,Kotlin的编译器会在Java字节码中引用基本数据类型
五、range表达式
//range
val age = 0
//0 .. 3 大于等于0小于等于3
if(age in 0 .. 3){println("婴幼儿")
//4 .. 12 大于等于4小于等于12
}else if(age in 4 .. 12){println("少儿")
}else{println("青少年")
}//其他写法
if(age !in 0 .. 3){}else{}
range表达式是闭区间
六、when表达式
//whenval school = "小学"when(school){"学前班" -> "幼儿园""小学" -> "少儿""中学" -> "青少年"else ->{println("未知")}}
允许你编写条件式,在某个条件满足时,执行相应的代码。只要代码包含else if
分支,都建议改用when表达式。
七、string模板
模板支持在字符串的引号内放入变量值,还支持字符串里计算表达式的值并插入结果,添加在${}
中的任何表达式,都会作为字符串的一部分求值。
//String模板 StringTemplate
val orgin = "Jack"
val dest = "Rose"
println("$orgin love $dest")val flag = true
println("Answer is ${if(flag) "我可以" else "对不起"}")
八、函数
可见修饰符默认是public
8.1、函数参数
默认参数
如果不打算传入值参,可以预先给参数指定默认值
具名函数参数
如果使用命名值参,就可以不用管值参的顺序
private fun doSomething(age: Int, flag: Boolean): String{return "result"
}//函数可以加默认值
fun fix(name: String, age: Int = 2){println(name + age)
}fun main(){doSomething(flag = false, age = 12)
}
8.2、Unit函数
不是所有函数都有返回值,Kotlin中没有返回值的函数叫Unit函数,也就是说他们的返回类型是Unit。在Kotlin之前,函数不返回任何东西用void描述,意思是“没有返回类型,不会带来什么,忽略它”,也就是说如果函数不反悔任何东西,就忽略类型。但是,void这种解决方案无法解释现代语言的一个重要特征,泛型。
8.3、TODO函数
TODO函数的任务就是抛出异常,就是永远别指望它运行成功,返回Nothing类型
public inline fun TODO(): Nothing = throw NotImplementedError()
8.4、反引号的函数名
Kotlin可以使用空格和特殊字符对函数命名,不过函数名要用一对反引号括起来
为了支持Kotlin和Java互操作,而Kotlin和Java各自却有着不同的保留关键字,不能作为函数名,使用反引号括住函数名就能避免任何冲突
fun `is`(){}
九、匿名函数
定义时不取名字的函数,我们称之为匿名函数,匿名函数通常整体传递给其他函数,或者从其他函数返回
匿名函数对Kotlin来说很重要,有了它,我们能够根据需要制定特殊规则,轻松定制标准库里的内置函数
//如果一个函数的lambda参数排在最后,或者是唯一的参数,那么括住lambda值参的一对圆括号就可以省略//字符串中s的个数val total = "Mississipi".count({letter -> letter == 's'})println(total)
匿名函数也有类型,匿名函数可以当作变量赋值给函数变量,就像其他变量一样,匿名函数就可以在代码里面传递了。
和具名函数不一样,除了极少数情况外,匿名函数不需要return关键字来返回数据,匿名函数会隐式或自动返回函数体最后一行语句的结果。
//声明一个变量,等于一个函数,就是函数的类型
//函数的类型由参数和返回值决定
val blessingFunction: ()->String = {val holiday = "New Year.""Happy $holiday"
}
println(blessingFunction())//传一个参数的时候,name参数可以用it替代
val blessingFunction2: (String)->String = {name ->"Happy $name"
}
println(blessingFunction2("New Year"))//返回值类型推断
val blessingFunction3 = {name: String ->"Happy ${name}"
}
println(blessingFunction3("New Year"))
定义只有一个
参数的匿名函数时,可以使用it关键来表示参数名。当你需要传入两个值参,it关键字就不能用了。
9.1、lambda
我们将匿名函数称为lambda,将它的定义成为lambda表达式,它返回的数据称为lambda结果。
十、定义参数是函数的函数
函数的参数是另外一个函数
//显示促销的文案,文案由另外一个函数生成
fun showOnBoard(goodsName: String, getDiscountWords: (String, Int) -> String){val hour = (1..24).shuffled().last();println(getDiscountWords(goodsName, hour))}fun test03(){val getDiscountWords = { goodsName: String, hour: Int ->val currentYear = 2027"${currentYear}年, 双11${goodsName}促销倒计时:$hour 小时"}showOnBoard("卫生纸", getDiscountWords)}//第二种写法 简略写法
showOnBoard("卫生纸"){ goodsName: String, hour: Int ->val currentYear = 2027"${currentYear}年, 双11${goodsName}促销倒计时:$hour 小时"
}
闭包是为了解决作用域问题
十一、函数内敛(inline)
在jvm上,你定义的lambda会以对象实例的形式存在,jvm会为所有同lambda打交道的变量分配内存,这就是内存开销。更糟的是,lambda的内存开销会带来严重的性能问题。幸运的是,kotlin有一种优化机制叫内敛,有了内敛,jvm就不需要使用lambda对象实例了,因而避免了变量内存分配。哪里需要使用lambda,编译器就会将函数体复制粘贴到哪里。
使用lambda的递归函数无法内敛,因为会导致粘贴无限循环,编译会发出警告。
十二、函数引用
fun getDiscountWords(goodsName: String, hour: Int): String{val currentYear = 2027return "${currentYear}年, 双11${goodsName}促销倒计时:$hour 小时"
}//显示促销的文案,文案由另外一个函数生成
fun showOnBoard(goodsName: String, getDiscountWords: (String, Int) -> String){val hour = (1..24).shuffled().last();println(getDiscountWords(goodsName, hour))}showOnBoard("卫生纸", ::getDiscountWords)
十三、闭包
在Kotlin中,匿名函数能修改并引用定义在自己的作用域之外的变量,匿名函数引用着定义自身的函数里的变量,Kotlin中的lambda就是闭包
能接受函数或者返回函数的函数又叫高级函数,高级函数广泛应用于函数式编程当中
//闭包
fun configDiscountWords(): (String) -> String{return {goodsName: String ->val currentYear = 2027val hour = (1..24).shuffled().last();"${currentYear}年, 双11${goodsName}促销倒计时:$hour 小时"}}val getDiscountWords = configDiscountWords()
println(getDiscountWords("牙膏"))
函数类型能让开发者少写模式化代码,写出更灵活的代码。java8支持面向对象编程和lambda表达式,但不支持将函数作为参数传给另一个函数或变量,不过java的替代方案是匿名内部类。
十四、null
kotlin更多地把运行时可能会出现的null问题,以编译时错误的方式,提前在编译期强迫我们重视起来,而不是等到运行时报错,防范于未然,提高了我们程序的健壮性。
对于null值问题,Kotlin反其道而行之,除非另有规定,变量不可为null值,这样一来,运行时崩溃从根源上得到解决。
var str: String = "abc"
str = null //编译报错var str2: String? = readLine()
str2 = null
Kotlin区分可空类型和非可空类型,所以,你要一个可空类型变量运行,而它又可能不存在,对于这种潜在危险,编译器时刻警惕着,为了应对这种风险,kotlin不允许你在可空类型上调用函数,除非你手动接手安全管理。
三种类型操作空值
- 选项一:安全调用操作符
?.
如果遇到null值,它就跳过函数调用,而不是返回null。
var str2: String? = readLine()?.capitalize()
-
使用非空断言操作符
!!.
-
使用
if
判断
十五、空合并操作符 ?:
如果左边的求职结果为null,就使用右边的结果值
var str: String? = str?: "butterfly"
十六、异常
//自定义异常
class UnskilledException(): IllegalStateException("操作不合法"){}fun checkOperation(number: Int?){number ?: throw UnskilledException()
}fun testException(){var number: Int? = null//处理异常try {number!!.plus(1)}catch (e: Exception){println(e)}}
十七、先决条件函数
Kotlin标准库提供了一些便利函数,使用这些内置函数,你可以抛出带自定义信息的异常,这些便利函数叫做先决条件函数,你可以用它定义先决条件,条件必须满足,目标代码才能执行
kotlin都是运行时异常(未检查异常),可以不try catch
,非运行异常(检查异常)都是通过编译器报错来处理的。
十八、字符串操作
18.1、substring
字符串截取,substring函数支持IntRange类型(表示一个函数范围的类型)的参数,until创建的范围不包括上限值。
val name = "Jimmy's friends"
val index = name.indexOf('\'')
//支持IntRanger
val str = name.substring(0 until index)
18.2、split和解构赋值
split函数返回的是List集合数据,List集合又支持解构语法特性,它允许你在一个表达式里给多个变量赋值,解构常用来简化变量的赋值。
//解构赋值
val name = "Jimmy,friends,hello"
val (a, b, c) = name.split(',')
println("$a,$b,$c")
18.2、replace 字符串替换
val name = "Jimmy,friends,hello"
val str = name.replace(Regex("[aeiou]")) {when(it.value){"a" -> "8""e" -> "4"else ->it.value}
}
println(str)
18.3、字符串比较
在kotlin中,用==
检查两个字符串中的字符是否匹配,用===
检查两个变量是否指向堆上同一个对象,而在Java中==
做引用比较,做结构比较时用equals
方法。
val str1 = "Jack"
val str2 = "Jack"
println(str1 == str2) //true
println(str1 === str2) //trueval str1 = "Jack"
val str2 = "jack".capitalize()
println(str1 == str2) //true
println(str1 === str2) //false
18.4、forEach
val name = "Jimmy,friends,hello";
name.forEach {println("$it*")
}
十九、数字类型
和Java一样,Kotlin中所有数字类型都是有符号的,也就是说既可以表示正数,也可以表示负数
19.1、安全转换函数
kotlin提供了toDoubleOrNull
和toIntOrNull
这样的安全转换函数,如果数值不能正确转换,与其触发异常不如干脆返回null值
val number:Int? = "8.98".toIntOrNull() //null
19.2、Double类型格式化
格式化字符串是一串特殊字符,它决定该如何格式化数据。
//8.99 会四舍五入
val s = "%.2f".format("8.987899")
//精度损失
val number2:Int? = 8.98.toInt()
//四舍五入
val number3:Int? = 9.98.roundToInt()
二十、标准库函数
20.1、apply
apply函数可看作一个配置函数,你可以传入一个接收者,然后调用一系列函数来配置它以便使用,如果提供lambda给apply函数执行,它会返回配置好的接收者
val file = File("D://I have a dream_copy.txt")
file.setReadable(true)
file.setWritable(true)
file.setExecutable(false)val file2 = File("D://I have a dream_copy.txt").apply {setReadable(true)setWritable(true)setExecutable(false)}
可以看到,调用一个个函数类配置接收者时,变量名就省掉了,这是因为,在lambda表达式里,apply能让每个配置函数都作用于接收者,这种行为有时又叫做相关作用域,因为lambda表达式里的所有函数调用都是针对接收者的,或者说,它们是针对接收者的隐式调用。
20.2、let函数
let函数能使某个作用于其lambda表达式里,让it关键字能引用它。let与apply比较,let会把接收者传给lambda,而apply什么都不传,匿名函数执行完,apply会返回当前接收者,而let会返回lambda的最后一行。
val result = listOf(3, 2, 1).first().let { it * it }
20.3、run函数
run和apply差不多,但与apply不同,run函数不返回接收者,run返回的是lambda结果,也就是true或者false。
val file = File("D://I have a dream_copy.txt")file.run {readText().contains("great")}val flag:Boolean = "I have a dream_copy".run(::isTooLong)//链式调用写法"I have a dream_copy".run(::isTooLong).run(::showMessage).run(::println)
20.4、with函数
with函数是run的变体,他们的功能行为是一样的,但with的调用方式不同,调用with时需要值参作为其第一个参数传入。
with("I have a dream_copy"){length > 0
}
20.5、also
also函数和let函数功能相似,和let一样,also也是把接收者作为值参传给lambda,但有一点不同:also返回接收者对象,而let返回lambda结果。因为这个差异,also尤其适合针对同一原始对象,利用副作用做事,既然also返回的是接收者对象,你就可以基于原始接收者对象执行额外的链式调用。
var fileContents: List<String>File("D://I have a dream_copy.txt").also {println(it.name)}.also { fileContents = it.readLines()}println(fileContents)
20.6、takeIf
和其他标准函数有点不一样,takeIf函数需要判断lambda中提供的条件表示式,给出true或false结果,如果判断结果式true,从takeIf函数返回接收者对象,如果式false,则返回null。如果需要判断某个条件是否满足,再决定是否可以赋值变量或执行某项任务
,takeIf就非常有用,概念上讲,takeIf函数类似于if语句,但它的优势是可以直接在对象实例上调用,避免了临时变量赋值的麻烦。
var fileContents = File("D://I have a dream_copy.txt").takeIf { it.exists() }?.readText()
20.7、takeUnless
takeIf辅助函数takeUnless,只有判断你给定的条件结果是false,takeUnless才是返回原始接收者对象。
var fileContents = File("D://I have a dream_copy.txt").takeUnless { it.isHidden }?.readText()
二一、List
getOrElse是一个安全索引取值函数,它需要两个参数,第一个是索引值,第二个是能提供默认值的lambda表达式,如果索引值不存在的话,可用来代替异常。
getOrNull是Kotlin提供的另一个安全索引取值函数,它返回null结果,而不是抛出异常。
//list集合 只读
val listOf = listOf("Jason", "Jack", "Jacky").distinct() //distinct去重
//越界了不会抛异常,而是可以交给lambda处理
listOf.getOrElse(4){"UnKnow"}
listOf.getOrNull(4)?: "UnKnow"
listOf.toMutableList()//可写
val mutableListOf = mutableListOf("Jason", "Jack", "Jacky")
mutableListOf.add("Jimmy")
mutableListOf.remove("Jimmy")
mutableListOf += "Jimmy"
mutableListOf -= "Jimmy"
mutableListOf.toList()
21.1、mutator函数
能修改可变列表的函数有个统一的名字:mutator函数
添加元素运算符与删除元素运算符
基于lambda表达式指定的条件删除元素
21.2、集合遍历
for in
遍历
forEach
遍历
forEachIndexed
遍历时要获取索引
val listOf = listOf("Jason", "Jack", "Jacky").distinct() //distinct去重
listOf.forEach{println(it)
}
listOf.forEachIndexed{index, item ->//带索引println("$index $item")
}
21.4、解构
通过_
符号过滤不想要的元素
val (origin, _, proxy) = listOf("Jason", "Jack", "Jacky").distinct() //distinct去重
二二、set
通过setOf创建set集合,使用elementAt函数读取集合中的元素
//只读
val set = setOf("Jack", "Jason", "Jacky")
set.elementAt(1)//可写
val mutableSetOf = mutableSetOf("Jack", "Jason", "Jacky")
mutableSetOf += "Kit"//快捷函数去重
listOf("Jason", "Jack", "Jack", "Jacky").distinct() //去重
22.1、数组类型
Kotlin提供各种Array,虽然是引用类型,但可以编译成Java基本数据类型
val intArrayOf = intArrayOf(10, 30, 40, 20)
listOf(10, 20, 30).toIntArray()
val arrayOf = arrayOf(File("xx"))
二三、map
to看上去像关键字,但事实上,它是个省略了点号和参数的特殊函数,to函数将它左边和右边的值转换成一对Pair。
//只读
val mapOf = mapOf("Jack" to 18, "Jo" to 20, "Ka" to 30)
//可变
val mutableMapOf = mutableMapOf("Jack" to 18, "Jo" to 20, "Ka" to 30)
mutableMapOf += Pair("Jas", 18)
mutableMapOf.put("Jimmy", 31)
mutableMapOf.getOrPut("Jac"){18}
23.1、读取Map的值
[]取值运算符,读取键对应的值,如果键不存在就返回null
getValue,读取对应的值,如果键不存在就抛出异常
getOrElse,读取键对应的值,或者使用匿名函数返回默认值
getOrDefault,读取键对应的值,或者返回默认值
mapOf.getValue("Jimmy")
mapOf.getOrElse("Jimmy"){10}
mapOf.getOrDefault("Jimmy", 10)mapOf.forEach{println("${it.key}, ${it.value}")
}mapOf.forEach{(key, value) ->println("${key}, ${value}")
}
二四、field
针对你定义的每一个属性,Kotlin都会产生一个field,一个getter、以及setter,field用来存储属性数据,你不能直接定义field,Kotlin会封装field,保护它里面的数据,只暴露给getter和setter使用。属性的getter方法决定你如何读取属性值,每个属性都有getter方法,setter方法决定你如何给属性赋值,所以只有可变属性才有setter方法,尽管Kotlin会自动提供默认的getter和setter方法,但在需要控制如何读写属性数据时
,你也可以自定义他们。
class Player{var name = "abc"get() = field.capitalize()set(value) {field = value.trim()}var age = 10get() = field.absoluteValueset(value) {field = value.absoluteValue}//计算属性是通过一个覆盖的get或set运算符来定义,这时field就不需要了val rolledValue//每次都是变动的get() = (1..6).shuffled().first()}
更多推荐
Kotiln基础语法总结
发布评论