kotlin常用语法扫盲及开发注意点,勿错失(持续更新)
::双冒号作用
双冒号操作符
表示把一个方法当做一个参数,传递到另一个方法中进行使用,通俗的来讲就是引用一个方法。
在kotlin里可以直接引用方法名当参数使用。
例如
fun main(args: Array<String>) {
println(testFun("hello", "world!", ::getResult))
}
fun getResult(str1: String, str2: String): String = "result is {$str1 , $str2}"
fun testFun(p1: String, p2: String, method: (str1: String, str2: String) -> String): String {
return method(p1, p2)
}
总结
直接引用getResult方法当参数使用传入testFun方法里,可以在testFun方法里直接调用。
注意引用的函数类型
要匹配,或者说要确定参数个数、类型、返回值都和其形参一致。
输入结果:result is {hello, world}
map,list使用
经常使用map和list集合,set很少使用。
只读map
Map也分为只读map和可变两种Map(mutableMap)。实际是MutableMap
继承自Map
,是它的子类,扩展的增加/修改/删除操作。
Kotlin建议使用Map和MutableMap,不建议使用HashMap。
map创建
val map= mapOf<String, String>("name" to "cy", "age" to "12")
创建之后,定长,不能删除和修改。
map取值
mapp.getValue("name")
或
mapp["name"]
2者相当
map遍历
for ((k,v) in map){
println("$k -> $v")
}
可变MutableMap
创建MutableMap
val mcc:MutableMap<String, String> = mutableMapOf<String, String>("name" to "cy", "age" to "12")
创建之后,可以删除和删除。如下
mcc.put("address", "china")
mcc.remove("address")
遍布
Map和MutableMap遍历方式都一样的。
遍历key
for (key in map.keys) {
Log.i("tag","key = $key")
}
遍历value
for (value in map.values) {
Log.i("tag","value = $value")
}
遍历整个结构点
方式一
for ((k,v) in map){
println("$k -> $v")
}
k
是键,v
是值的意思,2个都可以自己命名,取值使用$k
和$v
。
方式二
val setEntry = map.entries
setEntry.forEach{
println("key: ${it.key} , value: ${it.value}")
}
list
list和map类似,也分为只读List和可变两种List(mutableList)。
只读list
创建
val testList= listOf<String>("name", "age","address")
遍历
方式一for循环
for (nm in testList) {
println(nm)
}
方式二迭代器
val ite:Iterator = testList.iterator()
while (iterator.hasNext()) {
println(iterator.next())
}
方式三lamdba表达式
val arrayDouble = testList.map(value -> println(value))
如果括号中的参数只有一个,那么完全可以省略括号,同时其变量可以用it来代替,还可省略“->”,如下
val arrayDouble = testList.map(println(it.value))
常用的数组、集合、序列等数据结构的扩展函数
filter()
val list = listOf(1, 2, 3);
var filterList = list.filter(it >=2 );
pritln(filterList)
结果为:2,3
。
函数作用是过滤,接收函数作为参数,遍历集合每个元素,将符合条件的元素重新封装到一个集合里返回。
map()
映射,用于转化数据或者说计算新数据,遍历每个元素,根据传入的函数计算一个新值组成新的集合返回给变量
val list = listOf(1, 2, 3);
var filterList = list.map(it *2 );
pritln(filterList)
输出结果:2,4,6
泛型函数
泛型就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
kotlin类可以指定泛型
class Box<T>(t: T) {
var value = t
}
使用时传入具体类型即可
val box: Box<Int> = Box<Int>(1)
泛型函数即在函数前加上泛型,比如kotlin里let函数的申请如下
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
T和R是后面函数里传入的参数类型,要调用泛型函数,在调用处函数名之后指定类型参数即可。
this.let<String, int>()
泛型函数常见去标准库源码里。
常见内联函数
let函数
是个内联函数,源码定义是
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
接收一个函数做为参数,说明let是高阶函数,同时接收的函数类型用lambda表达式指定,将T类型值转换成R类型值。
我们知道它是一个泛型函数,一般能通过kotlin的智能推算知道T和R的类型,所以使用let函数时不指定类型。
一般在let后面直接跟上block代码块,在函数块内可以通过 it 指代该对象。返回值为函数块的最后一行或指定return表达式。
let函数可以使用在任何对象上。
let函数在如下2种情况下使用
- 最常用的场景就是使用let函数处理需要针对一个
可null的对象统一做判空处理
。 - 然后就是需要去明确一个变量所处特定的作用域范围内可以使用
使用示例
//表示object不为null的条件下,才会去执行let函数体
object?.let{
it.todo()
...
}
//在函数体内使用it替代object对象去访问其公有的属性和方法
object.let{
it.todo()
...
}
with函数
标准语法
with(object,{
//todo
})
with函数一般使用结构,因为函数体是是后一个参数,可以放在外面。
with(object){
//todo
}
它是将某对象作为函数的参数,在函数块内可以通过 this 指代该对象,可以在函数体内直接使用对象属性和方法。返回值为函数块的最后一行或指定return表达式。
但是它没有做判空操作,需要手动判空。
适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可。
与let区别
- 函数体内部使用this代表对象,而不是it
- 没有做判空操作,需要手动判空。
- 使用的地方不是很多
run
run函数使用的一般结构
object.run{
//todo
}
//判空
object?.run{
//todo
}
run函数实际上可以说是let和with两个函数的结合体,适用于let,with函数任何场景。它弥补了let函数在函数体内必须使用it参数替代对象,在run函数中可以像with函数一样可以省略,直接访问实例的公有属性和方法,另一方面它弥补了with函数传入对象判空问题,在run函数中可以像let函数一样做判空处理。
返回值为函数块的最后一行或指定return表达式。
apply
run函数使用的一般结构
object.apply{
//todo
}
//判空
object?.apply{
//todo
}
看apply函数和run函数很像,唯一不同点就是它们各自返回的值不一样,run函数是以闭包形式返回最后一行代码的值,而apply函数的返回的是传入对象的本身。
区别
Tables | Are | Cool |
---|---|---|
col 3 is | right-aligned | $1600 |
col 2 is | centered | $12 |
zebra stripes | are neat | $1 |
函数名 | 定义inline的结构 | 函数体内使用的对象 | 返回值 | 是否是扩展函数 | 适用的场景 |
---|---|---|---|---|---|
let | fun T.let(block: (T) -> R): R = block(this) | it指代当前对象 | 闭包形式返回 | 是 | 适用于处理不为null的操作场景 |
with | fun with(receiver: T, block: T.() -> R): R = receiver.block() | this指代当前对象或者省略 | 闭包形式返回 | 否 | 适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可,经常用于Android中RecyclerView中onBinderViewHolder中,数据model的属性映射到UI上 |
run | fun T.run(block: T.() -> R): R = block() | this指代当前对象或者省略 | 闭包形式返回 | 是 | 适用于let,with函数任何场景。 |
apply | fun T.apply(block: T.() -> Unit): T { block(); return this } | this指代当前对象或者省略 | 返回this | 是 | 1、适用于run函数的任何场景,一般用于初始化一个对象实例的时候,操作对象属性,并最终返回这个对象。2、动态inflate出一个XML的View的时候需要给View绑定数据也会用到.3、一般可用于多个扩展函数链式调用 4、数据model多层级包裹判空处理的问题 |
Kotlin中常见的符号
参考关键字与操作符
$
符号
varName 表示变量值
{object.name} 表示对象属性返回值
?
符号
赋值和返回值
//在变量类型后面加上问号,代表该变量是可赋值为null,否则不能赋值为null
var name: String? = "zhangsan"
//方法的返回类型后加上问号,代表方法可以返回null
fun parseInt(str: String): Int? {
// (代码略)
}
//b为非空,则返回b.length ,否则返回 null
b?.length
安全空访问?.
person?.name
person对象不为null时才会调用它的name属性.
Elvis操作?:
符号
?:
做空判断处理
val nullableName: String? = ...
val name: String = nullableName ?: "default_name"
安全类型转换as?
如果想安全地进行类型转换, 当转换失败时结果 null, 这种情况可用 as?
val location: Any = "London"
val safeString: String? = location as? String
val safeInt: Int? = location as? Int
!!
符合
这会返回一个非空的 b 值 或者如果 b 为空,就会抛出一个 NPE 异常.
val l = b!!.length
==
号与===
号
==
判断值是否相等,===
判断值及引用地址是否完全相等。
..
符合
..
配合in和!in一起使用,表示区间的意思.
for(i in 1..10) { // 等同于 1 <= i && i <= 10
println(i)
}
//使用until函数,创建一个不包括其结束元素的区间
for (i in 1 until 10) { // i in [1, 10) 排除了 10
println(i)
}
// 使用 step 指定步长
for (i in 1..4 step 2) {
print(i) // 输出“13”
}
//由大到小,使用downTo
for (i in 4 downTo 1 step 2){
print(i) // 输出“42”
}
_
(下划线)符合
用于对象解析时,属性的占位符
例如
data class Person(var id: Int, var name: String)
val (_,name) = Person(12,"cy")
::
符号
双冒号操作符
表示把一个方法当做一个参数,传递到另一个方法中进行使用,通俗的来讲就是引用一个方法。见文章开头。
@符号
在kotlin标签语法中使用。
在 Kotlin 中任何表达式都可以用标签(label)来标记。 标签的格式为标识符后跟 @ 符号,例如:abc@
标签语法经常和break和return使用,用于指定从标签处中断或从标签处返回。
//直接在loop标签处中断(最外层中断for循环)
loop@ for (i in 1..100) {
for (j in 1..100) {
if (……) break@loop
}
}
//从lit标签处返回
ints.forEach lit@ {
if (it == 0) return@lit
print(it)
}
{}
符号
{}符号中定义lambda表达式
Lambda表达式和匿名函数
Lambda表达式和匿名函数很相似,但其实2者不相等。Lambda表达式可以称为匿名函数,但匿名函数不能称为Lambda表达式。
kotlin代码
fun compare(a: Int, b: Int): Int{
return a +b
}
add(::compare)
计算2者和。
- 修改为匿名函数
fun compare(a: Int, b: Int): Int{
return a +b
}
add((a,b)->{a+b})
- 修改为Lambda表达式
fun compare(a: Int, b: Int): Int{
return a +b
}
add({(a,b)->a+b})
如果没有入参数,前面的括号可以去掉。比如add({a+b})
Lambda表达式满足以下条件即可。
- 一般使用“
{ }
”包围。 - 它的参数(如果有的话)在“->”前定义,参数类型可能是省略的。
- 函数体跟在“->”后面。
- 返回值是最一个语句值,或显示使用
return
指定的值
匿名函数只是省略了函数名,但整个函数结构并不是被大括号{}
包裹的。所以匿名函数不是Lambda表达式。
高阶函数
高阶函数是将函数作为参数或返回一个函数。可以使用Lambda表达式和匿名函数。
上面的add函数就是高阶函数。如下
fun add(cc:(a,b)->a +b){
cc()
}
指定函数类型的入参类型和返回类型。
常见的高阶函数除了上面介绍的let,run,with,apply外还有下面的。
val oldList = listOf(1, 2, 3, 4, 5)
val newList = ArrayList<Int>()
//forEach 遍历--高阶函数
oldList.forEach {
val newElement = it * 2 + 3
newList.add(newElement)
}
//map映射返回新的集合--高阶函数
val newList1 = oldList.map {
it * 2 + 3
}
//filter 过滤返回新的集合--高阶函数
val newList5 = oldList.filter {
it == 2 || it == 4
}