这篇文章简单梳理下Kotlin的语法糖。
一、内置类型
1.1 基本类型
- 整数类型:Byte、Short、Int和Long,Int是默认类型
- 浮点类型:Float和Double,Double是默认类型
- 字符类型:Char
- 布尔类型:Boolean
增加无符号类型:UByte、UShort、Uint、ULong
Kotlin数据基本类型均为对象类型。
Unit 等同于void,Any等同于Object,这是kotlin与java区别的地方。
- var 变量
- val 只读变量 //作为局部变量等价于java final常量,而作为全局变量可以配置get方法,return值可以不同。
- const val 全局静态常量
类型自动推导
var b:Int =2 推导为:var a = 2
注:不能直接声明var a,要么初始化var a = 2,要么声明好类型var a : Int
强制类型转换
var l = 1L
var d = l.toDouble() //这里不像java,类型转换必须主动调用toXXX()
字符串比较:
== 比较内容 equals
===比较地址 ==
字符串模版
val c = "world"
println("hello $c”)
Raw字符串
val n = """
<!doctype html>
<html>
<head>
<meta charset="UTF-8"/>
</head>
<body>
<div id="container">
<H1>Hello World</H1>
</div>
</body>
</html>
""".trimIndent()//trim掉公共的空白
别名
typealias ArrayList<E> = java.util.ArrayList<E>
空类型安全
操作符 | 描述 |
---|---|
? | 可空操作符,声明可空类型。 var str:String? = "aaa" |
?. | 安全调用操作符,为空返回null。str?.length |
?.let{} | ?.与let一起使用,用于遍历集合时,则忽略null值,只对非空值执行操作。 for (item in listWithNulls) { item?.let { println(it) } // 输出 abc 并忽略 null } |
?: | Elvis操作符,检查结果为空返回后面的值。val a = b?.length ?: 0 |
!! | 非空断言运算符,将任何值转换为非空类型,若该值为空则抛出NPE。 |
as? | 尝试转换成指定类型,如果尝试转换不成功则返回null。var b: Int ? = a as? Int |
filterNotNull | 过滤一个可空类型元素集合中的非空元素。val list: List<Int> = nullableList.filterNotNull() |
另外普调类型与对应的空类型的继承关系:
String 是String?的子类
var x:String = “Hello”
var y:String? = “World”
x = y // 不行,编译器报type mismatch错误
y = x //可以
String! 为平台类型,无法判断是否为空,需要开发者自己去做安全访问。
附:看类型快捷键 shift+control+p
1.2 数组
IntArray //int[]
Array<Int> //Integer[] 对象类型 Array<Person>
数组的创建
val c0 = intArrayOf(1, 2, 3, 4, 5) //int[] arr = {1,2,3,4,5}
val c1 = IntArray(5) // int[] arr = new int[5]
val c2 = Array<Int>(5,{it})// 第二个参数是方法类型, init: (Int) -> T
数组的遍历
for each:
for (e in c0) {
println(e)
}
c0.forEach {
println(it)
}
for i:
for (i in c0.indices) {
//i是对应的index
println(c0[i])
if(1 in e){// 对应元素是否在数组中,!in 对应元素不在数组中
println(“1 exists in variable e"])
}
}
1.3 区间
区间
val intRange = 1..10 // [1, 10]
val charRange = 'a'..'z'
开区间
val intRangeExclusive = 1 until 10 // [1, 10)
val charRangeExclusive = 'a' until 'z'
倒序区间
val intRangeReverse = 10 downTo 1 // [10, 9, ... , 1]
val charRangeReverse = 'z' downTo 'a'
步长
val intRangeWithStep = 1..10 step 2
val charRangeWithStep = 'a'..'z' step 2
1.4 运算符
直接查看官方文档
https://kotlinlang.org/docs/reference/operator-overloading.html
二、 集合
2.1 集合框架
这里一种写法就用java写法就行,区别就是不用new
var list = ArrayList<String>()
list.add("1")
println(list.get(0))
var map = HashMap<String,String>()
map.put("key","value")
println(map.get("key”))
也可以按标准kotlin写法:
val list = listOf(1, 2, 3)//不可变list(不能添加和删除元素)
val mutableList = mutableListOf(1, 2, 3)//可变list
val arrays = mutableListOf<Int>()//仅仅初始化,没有元素,必须指定泛型类型
val map = mapOf( //不可变map
"a" to 1,
"b" to 2
)
val mutableMap = mutableMapOf( //可变map
"a" to 1,
"b" to 2
)
val maps = mutableMapOf<Int,String>()
list操作:
mutableList.add(4) //set
mutableList.forEach {
mutableList[2] = 5 //get
println(it)
}
map操作
mutableMap["c"] = 3//set
mutableMap["c"]//get
mutableMap.set("c", 3)//java式写法set
mutableMap.get("c")//get
同时注意:
add 操作可用 += 代替, 同理remove 为 -=
例如:mutableList += 4
这里单独介绍下循环
for循环表达式
for(item in arrays){//item是每个元素
print(item)
}
for(i in arrays.indices){
print(array[i])
}
while与do..while这部分与java没区别
2.2 集合变换与序列
2.2.1 集合的映射操作:filter、map、flatMap
filter:保留满足条件的元素
list.filter{it %2 == 0}
变换序列:list.asSequence().filter{it %2 == 0} //.asSequence()类似java的 .streammap:集合中的所有元素意义映射新集合
list.map{it2+1}
变换序列:list.asSequence().map{it2+1}flatMap:集合中的所有元素意义映射新集合,并合并这些集合得到新集合
list.flatMap{0 until it}.joinToString().let(::println)
.asSequence懒汉式操作案例:
val list = listOf(1, 2, 3, 4)
list.asSequence().filter {
println("filter:$it")
it % 2 == 0
}.map {
println("map:$it")
it * 2 + 1
}.forEach {
println("foreach:$it")
}
打印结果:
filter:1
filter:2
map:2
foreach:5
filter:3
filter:4
map:4
foreach:9
去掉.asSequence对比:
list.filter {
println("filter:$it")
it % 2 == 0
}.map {
println("map:$it")
it * 2 + 1
}.forEach {
println("foreach:$it")
}
打印结果:
filter:1
filter:2
filter:3
filter:4
map:2
map:4
foreach:5
foreach:9
这部分体会是有点类似于rxjava
2.2.2 集合的聚合操作 sum、reduce、fold
sum 所有元素求和
reduce 将元素依次按规则聚合,结果与元素类型一致
fold 给定初始化值,将元素按规则聚合,结果与初始化值类型一致
list.fold(StringBuilder()){
acc,i->acc.append(i)
}
三、表达式
3.1 if表达式
kotlin可以把if表达式的结果赋值给一个变量
val max = if (a > b) a else b
3.2 when表达式
when 将它的参数和所有的分支条件顺序比较,直到某个分支满足条件。
c = when (x) {
1 -> print("x等于1")
in 10..20 -> print(“x在区间10-20范围内")
else -> { //default
}
}
将条件转移到分支
var x:Any = …
c = when{
x is String -> c = x.length
x == 1 -> c = 100
else -> c = 20
}
括号内条件还能做赋值
c = when(var input = readLine()){ //since kotlin 1.3
null -> 0
else -> input.length
}
循环中才使用的返回与跳转,高阶函数forEach不能使用:
- return。默认从最直接包围它的函数或者匿名函数返回。
- break。终止最直接包围它的循环。
- continue。继续下一次最直接包围它的循环。
3.3 try…catch表达式
c = try{
a/b
}catch(e:Exception){
e.printStackTrace()
0
}
3.4 Lambda表达式
使用:
func()
Lambda表达式定义:
Kotlin
Lambda表达式参数省略
val f1:Function1<Int,Unit> = {p ->
println(p)
}
转为:
val f1:Function1<Int,Unit> = {
println(it)
}
3.5 SAM转换
一个参数类型问只有一个方法的Java接口的Java方法调用时可用lambda表达式做转换作为参数。
val eventManager = EventManager()
//匿名内部类:object:类型
val onEvent = object:EventManager.OnEventListener{
override fun onEvent(event:Int){
println(“onEvent $event")
}
}
eventManager.addOnEventListener(onEvent)
eventManager.removeEventListener(onEvent)
四、函数
kotlin函数有自己的类型,可以赋值、传递、并在合适的条件下调用
函数返回值支持类型推导。
4.1 函数类型:
4.2 函数的引用
变长参数:vararg
fun main(vararg args: String) {…}
4.3 多返回值
可以用Pair和Triple进行包装
Pair
val pair = "Hello" to "World"
val pair1 = Pair("Hello", "Kotlin")
val first = pair.first
val second = pair.second
val (x, y) = pair
Triple
val triple = Triple("a", 1, 1.0)
val first = triple.first
val second = triple.second
val third = triple.third
val (x, y, z) = triple
比如:
fun multiReturn():Triple<Int,Long,String>{
return Triple(1,3L,"aaa")
}
fun main() {
val (i, l, s) = multiReturn()
println(i+l)
}
默认参数和具名参数
fun defaultParameter(x: Int = 5, y: String, z: Long = 0L){...}
fun main() {
defaultParameter(y = "aaa")
}
4.4 高阶函数
参数类型包含函数类型或返回值类型为函数类型的函数为高阶函数
public inline fun <R> IntArray.map(transform: (Int) -> R): List<R> {//参数类型包含函数类型(Int) -> R,返回值类型包含函数类型List<R>
return mapTo(ArrayList<R>(size), transform)
}
函数类型为最后一个参数可以移到括号外面,且省略小括号:
intArray.forEach{ it -> Unit
println(it)
}
省略为:
intArray.forEach { print(it) }
而
intArray.forEach { print(it) } 又等价于 intArray.forEach(::print) //::print 匹配类型为(Int) -> Unit的函数引用,可直接传入
完整案例:
fun cost(block: () -> Unit) {
val start = System.currentTimeMillis()
block()
print("cost:${System.currentTimeMillis() - start}")
}
fun fibonacci(): () -> Long {
var first = 0L
var second = 1L
return {
val next = first + second
val current = first
first = second
second = next
current //return current
}
}
fun main(args: Array<String>) {
cost {
val fib = fibonacci()
for (i in 0..10) {
print(fib())
}
}
}
4.5 内联函数 inline
函数体内的多个函数执行优化为一个函数体来执行,提升代码执行性能。常用于高阶函数,将参数函数和自身方法体合和一个函数体来执行。
inline fun cost(block: () -> Unit) {
val start = System.currentTimeMillis()
block()
print("cost:${System.currentTimeMillis() - start}")
}
内联高阶函数的return
val ints = intArrayOf(1, 2, 3, 4)
ints.forEach {
if (it == 3) return@forEach //仅仅跳出这一次内联函数的调用相当于continue,continue只能用在循环中
print(it)
}
non-local return
ints.forEach {
if (it == 3) return //直接退出
print(it)
}
禁止non-local return :关键字 crossinline
inline fun Runnable(crossinline block:()->Unit):Runnable{ //也可以使用no inline block:()->Unit,禁止函数内联,这样前面的inline就没有意义了。
return object : Runnable{
override fun run(){
block()
}
}
}
内联函数的限制
- public/protected 的内联方法只能访问对应类的public成员
- 内联函数的内联函数参数不能被存储(赋值给变量)
- 内联函数的内联函数参数只能传递给其他内联函数参数
简而言之:
- public 只能访问public
- 内联只能访问内联
- 参数不能被存