符号
:
变量类型、函数、接口、返回值
定义变量
- 不可变变量
val a :Int
val a :Int = 4
- 可变变量
var b :String
- 不可变变量
继承基类
calss 子类 : 父类() {}
注意“
:
”后是父类的构造方法【父类带括号“()”】
实现接口
class 类 : 接口 {}
定义函数返回值
class 类 : 返回值类型{}
::
创建一个成员引用或者一个类引用
?
将类型标记为可空
?.
判空不处理
1 | val ages1 = age?.toInt() //空的话不处理 |
?:
判空取后面的值
1 | var data = lesson.data ?: "日期待定" |
如果
lesson.data
是null
则把后面的默认值赋值给data
,否则把lesson.data
赋值给data
例子:
1 | if(user.username?.length ?: 0 < 4){} |
user.username
是null
,那么user.username?.length
这个整体就是null
,就会得到0
;否则,user.username
有值则可以取到它的length
,然后判断是否<4
,就会忽略?:0
!
逻辑非
!!
非空断言运算符
将任何值转换为非空类型,若该值为空则抛出异常
->
- 分隔 lambda 表达式的参数与主体
- 分隔在函数类型中的参数类型与返回类型声明
- 分隔 when 表达式分支的条件与代码体
+
、-
、*
、/
、%
—— 数学操作符-/% 也用于将数组传递给 vararg 参数
it
kotlin
的lambda
更加简约
1 | //正常情况 |
reified
不再需要传参数clazz
比如定义实现一个扩展函数的启动Activity
,一般都需要传Class<T>
参数:
1 | //Function |
使用reified
方式
通过添加类型传递简化泛型参数
1 | //Function |
不安全的转换
kotlin中,使用 安装转换操作符as?
,它可以在失败时返回null
。如下,我们认为会安全的获取数据或返回null。然后如果获得的数据不是它期望的类型,这个函数会出现crash
1 | //Function |
如果不想crash的,修改如下
1 | //Function |
使用reified
方式
简化泛型参数和保证
as?
类型转换安全性
1 | //Function |
不同的返回类型函数重载
实现一个函数计算 DP 到像素,并返回一个 Int 或 Float。这种情况就会想到函数重载,如下所示:
1 | fun Resources.dpToPx(value: Int): Float { |
但是,这将导致编译时出错。原因是,函数重载方式只能根据参数计数和类型不同,而不能根据返回类型。
使用reified
方式
1 | inline fun <reified T> Resources.dpToPx(value: Int): T { |
Any
相当于Java中的Object,如果在泛型中还可以用“*”表示,如<*>
硬关键字
as
as?
用于安全类型转换
break
class
声明一个类
continue
do
开始一个 do/while 循环(后置条件的循环)
else
定义一个 if 表达式条件为 false 时执行的分支
false
指定布尔类型的“假”值
for
开始一个 for 循环
fun
声明一个函数
if
开始一个 if 表达式
in
- 指定在 for 循环中迭代的对象
- 用作中缀操作符以检测一个值属于一个区间、 一个集合或者其他定义“contains”方法的实体
- 在 when 表达式中用于上述目的
- 将一个类型参数标记为逆变
!in
- 用作中缀操作符以检测一个值不属于一个区间、 一个集合或者其他定义“contains”方法的实体
- 在 when 表达式中用于上述目的
interface
声明一个接口
is
!is
- 检测一个值不具有指定类型
- 在 when 表达式中用于上述目的
null
是表示不指向任何对象的对象引用的常量
object
同时声明一个类及其实例
package
指定当前文件的包
return
super
this
throw
true
指定布尔类型的“真”值
try
try、catch、finally
与java的一样(try、catch中都有return的情况)
- 如果try中报错那么走catch的return,再走finally(finally一定会走),finally后代码不执行
- 如果try中不报错,那么走try的return,再走finally(finally一定会走),finally后代码不执行
与java的一样(try、catch中没有return的情况)
- 如果try中报错:try–>catch–>finally,会走finally后的代码
- 如果try不报错:try–>finally,会走finally后的代码
1 | fun main() { |
typealias
声明一个类型别名
typeof
保留以供未来使用
val
var
val 和 var
val(value 的简写)用来声明一个不可变的变量,对应 Java 中的 final 变量。
var(variable 的简写)用来声明一个可变的变量,对应 Java 中的非 final 变量。
Kotlin 类型推导机制:val 关键字定义了个变量 a,给它赋值 10,a 就会自动推导成整型变量。若把字符串赋值给 a,a 就会自动推导成字符串变量。
1 | val a = 10 //(直接赋值)类型自动推导 |
延迟赋值:需要显式声明变量的类型
1 | var a: Int |
val 如果未在声明的时候初始化,之后可以初始化一次,之后再赋值编译不通过
var 可以多次赋值。不过只能赋值同种类型的,赋值不同类型编译器会报错。
when
开始一个 when 表达式(执行其中一个给定分支)
替代switch
when可以与else配合使用
while
开始一个 while 循环(前置条件的循环)
循环while、for、do-while
in
- in ‘A’..’F’
- (a, b) in treeMap集合
- !in
downTo
until
step
软关键字
以下符号在适用的上下文中充当关键字,而在其他上下文中可用作标识符:
by
catch
开始一个处理指定异常类型的块
constructor
声明一个主构造函数或次构造函数
delegate
用作注解使用处目标
dynamic
引用一个 Kotlin/JS 代码中的动态类型
field
用作注解使用处目标
file
用作注解使用处目标
finally
开始一个当 try 块退出时总会执行的块
get
- 声明属性的 getter
- 用作注解使用处目标
import
init
开始一个初始化块
lateinit
- 只能修饰
var
可读可写变量 - 声明的变量为不可控类型
- 声明的变量不能有初始值
- 声明的变量不能是基本数据类型
- 构造器中初始化的属性需要 lateinit 关键字
lateinit声明的var变量,使用的时候要用::xxx.isInitialized
进行判断是否初始化过了(然后直接进行判空后的处理),否则会抛出UninitializedPropertyAccessException
异常
lazy
- 只能修饰
val
常量。(懒加载:初始化方式已确定,只是在使用的时候执行。)
param
用作注解使用处目标
property
用作注解使用处目标
receiver
用作注解使用处目标
set
- 声明属性的 setter
- 用作注解使用处目标
setparam
用作注解使用处目标
where
内置函数let、also、with、run、apply
前言
在Kotlin
中,有一些用于扩展 & 方便开发者编码的内置函数,能大大提高开发者的开发效率。今天,我将主要讲解的是:
- let函数
- also函数
- with函数
- run函数
- apply函数
基础知识:接口回调中Lambda使用
在Kotlin中可使用Lambda函数简化一些不必要的嵌套接口回调方法
注:仅支持单个抽象方法回调,多个回调方法不支持。
1 | // Java接口回调 |
下面,我将讲解Kotlin
里提供用于扩展 & 方便开发者编码的几个有用内置函数:let函数、also函数、with函数、 run函数、apply函数。
let函数
定义
- 一个作用域函数
作用
- 定义一个变量在一个特定的作用域范围内
- 避免写一些判断
null
的操作
应用场景
- 明确一个变量所处特定的作用域范围内可使用
- 针对一个可
null
的对象统一做判空处理
使用方法
1 | // 作用1:使用it替代object对象去访问其公有的属性 & 方法 |
使用示例
1 | // 使用Java |
also函数
作用 & 应用场景
类似let函数,但区别在于返回值:
- let函数:返回值 = 最后一行 / return的表达式
- also函数:返回值 = 传入的对象的本身
使用示例
1 | // let函数 |
with函数
作用
调用同一个对象的多个方法 / 属性时,可以省去对象名重复,直接调用方法名 / 属性即可
应用场景
需要调用同一个对象的多个方法 / 属性
使用方法
1 | with(object){ |
使用示例
1 | // 此处要调用people的name 和 age属性 |
run函数
作用 & 应用场景
结合了let、with两个函数的作用,即:
- 调用同一个对象的多个方法 / 属性时,可以省去对象名重复,直接调用方法名 / 属性即可
- 定义一个变量在特定作用域内
- 统一做判空处理
使用方法
1 | object.run{ |
使用示例
1 | // 此处要调用people的name 和 age属性,且要判空 |
apply函数
作用 & 应用场景
与run函数类似,但区别在于返回值:
- run函数返回最后一行的值 / 表达式
- apply函数返回传入的对象的本身
应用场景
对象实例初始化时需要对对象中的属性进行赋值 & 返回该对象
使用示例
1 | // run函数 |
至此,关于Kotlin
里提供用于扩展 & 方便开发者编码的几个有用内置函数讲解完毕。
总结
可提前判空 | 可返回值 | 可重命名入参 | 定义inline的结构 | 返回值 | 是扩展函数 | 应用场景 | |
---|---|---|---|---|---|---|---|
let | Y | Y | Y | fun <T,R> T.let(block: (T)->R): R=block(this) |
闭包形式返回 | Y | 1. 明确一个变量所处特定的作用域范围内可使用 2. 针对一个可null的对象统一做判空处理 |
also | Y | N(返回自身) | Y | fun <T> T.also(block: (T)->Unit): T{block(this);return this} |
返回this | Y | 同上 |
run | Y | Y | N | fun <T,R> T.run(block: T.()->R): R=block() |
闭包形式返回 | Y | 1. 调用一个对象的多个方法/属性时,可省去对象名重复,直接调用方法名/属性即可 2. 定义一个变量在特定作用域内 3. 统一做判空处理 |
apply | Y | N(返回自身) | N | fun <T> T.apply(block: T.()->Unit): T{block();return this} |
返回this | Y | 对象实例初始化时需要对对象中的属性进行赋值&返回该对象 |
with | N | Y | N | fun <T,R> with(receiver: T,block: T.()->R): R=receiver.block() |
闭包形式返回 | N | 需要调用同一个对象的多个方法/属性 |
inline、noinline、crossinline的区别
其他
companion
Kotlin中的object 与companion object的区别
object
对象表达式;对象声明
对象表达式
1
2
3
4
5
6val tv = findViewById<TextView>(R.id.tv)
tv.setOnClickListener(object: OnClickListener{
override fun onClick(p0: View?){
Toast.makeText(this@MainActivity, "吐司内容", Toast.LENGTH_SHORT)
}
})对象声明
用
object
修饰的类为静态类,里面的方法和变量都是静态
的直接声明类
1
2
3
4
5
6object DemoManager{
private val TAG = "DemoManager"
fun a(){
Log.e(TAG, "此时 object 表示 直接声明类")
}
}声明静态内部类
类内部的对象声明,没有被
inner
修饰的内部类都是静态的1
2
3
4
5
6
7
8
9
10
11
12class DemoManager{
object MyObject{
fun a(){
Log.e(TAG, "此时 object 表示 声明静态内部类")
}
}
//kotlin中调用
fun init(){
MyObject.a()
}
}java中调用
1
MyObject.INSTANCE.a();
companion object
修饰为伴生对象。伴生对象在类中只能存在一个,类似于java中的
静态方法
1
2
3
4
5
6
7
8
9
10
11
12
13class DemoManager{
companion object{
private val TAG = "DemoManager"
fun b(){
Log.e(TAG, "此时 companion object 表示伴生对象")
}
}
//kotlin中调用
fun init(){
b()
}
}java中调用
1
DemoManager.Companion.b();
internal
internal修饰类的方法,表示这个类方法只适合当前module使用,其他module调用不到这个方法
sealed
- 密封类和它的子类必须定义在一个文件中
- 密封类是不能被初始化的
理解:父类只是一个组织者(对于子类来说)(除了这个功能他什么都做不了)甚至初始化都做不到,具体可以出面做事情的是子类