Kotlin 入门笔记03 | 青训营笔记

336 阅读5分钟

这是我参与「第四届青训营 」笔记创作活动的第11天

标准函数

Kotlin的标准函数指的是Standard.kt文件中定义的函数,任何Kotlin代码都可以自由地调用所 有的标准函数。

with

with函数接收两个参数:第一个参数可以是一个任意类型的对 象,第二个参数是一个Lambda表达式。with函数会在Lambda表达式中提供第一个参数对象的上下文,并使用Lambda表达式中的最后一行代码作为返回值返回。

 val result = with(obj) {
     // 这里是obj的上下文
     "value" // with函数的返回值
 }

在上下文环境中可以直接调用对象中的方法,而无需对象名

示例用法:整合集合中的字符串

 val list2 = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
 val result2 = with(StringBuilder()) {
     append("Start eating fruits.\n")
     for (fruit in list2) {
         append(fruit).append("\n")
     }
     append("Ate all fruits.")
     toString() // 返回最后结果
 }
 println(result2)

run

run函数的用法和使用场景其实和 with函数是非常类似的,只是稍微做了一些语法改动而已。首先run函数通常不会直接调用, 而是要在某个对象的基础上调用;其次run函数只接收一个Lambda参数,并且会在Lambda表 达式中提供调用对象的上下文。其他方面和with函数是一样的,包括也会使用Lambda表达式 中的最后一行代码作为返回值返回。

 val result3 = obj.run {
     // 这里是obj的上下文
     "value" // run函数的返回值
 }

使用示例:整合集合中的字符串

 val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
 val result4 = StringBuilder().run {
     append("Start eating fruits.\n")
     for (fruit in list) {
         append(fruit).append("\n")
     }
     append("Ate all fruits.")
     toString()
 }
 println(result4)

apply

apply函数和run函数也是极其类似的,都要在某 个对象上调用,并且只接收一个Lambda参数,也会在Lambda表达式中提供调用对象的上下 文,但是apply函数无法指定返回值,而是会自动返回调用对象本身。

 val result5 = obj.apply {
     // 这里是obj的上下文
 } // result5 就是一个 obj 

使用示例

 val list3 = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
 val result6 = StringBuilder().apply {
     append("Start eating fruits.\n")
     for (fruit in list3) {
         append(fruit).append("\n")
     }
     append("Ate all fruits.")
 }
 println(result6.toString())

安卓中的应用示例

启动Activity,这时传一个参数就要调用一次 putExtra() 函数

 val intent = Intent(context, SecondActivity::class.java)
 intent.putExtra("param1", "data1")
 intent.putExtra("param2", "data2")
 context.startActivity(intent)

使用 apply 简化

 val intent = Intent(context, SecondActivity::class.java).apply {
     putExtra("param1", "data1")
     putExtra("param2", "data2")
 }
 context.startActivity(intent)

静态方法

Kotlin语法糖实现静态特性

传统Java 中的静态方法推荐使用单例类来实现

 object Util {
     fun doAction() {
         println("do action")
     }
 }

调用

 Util.doAction()

但是使用单例类将会把该类所有的方法都变成类似静态方法的特性,如果只想将一部分方法变为静态方法,可以用 companion object

 class Util {
     fun doAction1() {
         println("do action1")
     }
     companion object {
         fun doAction2() {
             println("do action2")
         }
     }
 }

doAction2()方法其实也并不是静态方法,companion object这个关键字实际上会在Util类的内部创建一个伴生类,而doAction2()方法就是定义在这个伴生类里面的实例方法。只是Kotlin会保证Util类始终只会存在一个伴生类对象,因此调用Util.doAction2()方 法实际上就是调用了Util类中伴生对象的doAction2()方法。

由此可以看出,Kotlin确实没有直接定义静态方法的关键字,但是提供了一些语法特性来支持类 似于静态方法调用的写法。

如果实在需要真正的静态方法,可以使用注解顶层方法

注解实现单例

@JvmStatic注解 只能注解在单例类或者 companion object 的方法上,不能注解在普通方法上

 class Util {
     fun doAction1() {
         println("do action1")
     }
     companion object {
         @JvmStatic
         fun doAction2() {
             println("do action2")
         }
     }
 }

顶层方法

顶层方法指的是那些没有定义在任何类中的方法, 比如 main() 方法。

Kotlin编译器会将所有的顶层方法全部编译成静态方法。

首先需要创建一个Kotlin文件。对着任意包名右击 → New → Kotlin File/Class,在弹出的对话框中输入文件名即可。注意创建类型要选择File

 fun topMethod() {
     println("topMethod !")
 }

调用

 fun main(args: Array<String>) {
     topMethod() // 直接调用
 }

比较常用的是 单例类,companion object 和顶层方法,@JvmStatic注解用的比较少。

延迟初始化

Kotlin 为了保证程序安全设计了许多的特性,比如变量默认不可为空,默认为常量等等,这些设计有时会给编码时带来麻烦。

例子

 class MainActivity : AppCompatActivity(), View.OnClickListener {
     private var adapter: MsgAdapter? = null
     override fun onCreate(savedInstanceState: Bundle?) {
         adapter = MsgAdapter(msgList)
     }
     override fun onClick(v: View?) {
         adapter?.notifyItemInserted(msgList.size - 1)
     }
 }

这里我们将adapter设置为了全局变量,但是它的初始化工作是在onCreate()方法中进行 的,因此不得不先将adapter赋值为null,同时把它的类型声明成MsgAdapter?。

虽然我们会在onCreate()方法中对adapter进行初始化,同时能确保onClick()方法必然在 onCreate()方法之后才会调用,但是我们在onClick()方法中调用adapter的任何方法时仍 然要进行判空处理才行,否则编译肯定无法通过。

要解决这个问题,可以使用 lateinit 关键字对全局变量进行延迟初始化。

 class MainActivity : AppCompatActivity(), View.OnClickListener {
     private lateinit var adapter: MsgAdapter
     override fun onCreate(savedInstanceState: Bundle?) {
         adapter = MsgAdapter(msgList)
     }
     override fun onClick(v: View?) {
         adapter.notifyItemInserted(msgList.size - 1)
     }
 }

如果在初始化前使用了该全局变量,会抛出 UninitializedPropertyAccessException 异常

可以使用 ::adapter.isInitialized 来判断是否初始化

 override fun onCreate(savedInstanceState: Bundle?) {
     if (!::adapter.isInitialized) { // 发现全局变量还没初始化则立刻初始化
         adapter = MsgAdapter(msgList)
     }
 }