【10】Kotlin语法进阶——标准函数和静态方法

提示:此文章仅作为本人记录日常学习使用,若有存在错误或者不严谨得地方,欢迎各位在评论中指出。

一、标准函数

在Kotlin语言中,标准函数是指由Kotlin标准库提供的一系列函数。这些函数包括各种操作,例如字符串处理、数字运算、集合操作等。这些函数可以直接在你的代码中使用,无需额外导入或定义。

1.1 标准函数let

let标准函数的作用是将调用它的对象作为参数传递到Lambda表达式中。let函数适用于在一个对象上执行多个操作的场景,它的示例代码如下:

①myObject.let{
    it.toDo()
}

在上面的①号例子中,myObject对象调用了let函数,然后Lambda表达式中的代码就会立即执行,并且将调用let函数的对象myObject作为参数传入Lambda表达式中
②let函数一般还会搭配判空符号?.进行使用,可以在空指针检查的时候起到很大的作用。例如我们有一个doStudy函数:

fun doStudy(study: Study?) {
    study?.readBooks()
    study?.doHomework()
}

上面这段代码其实很繁琐,因为我们每次调用study的方法都需要通过判空符号?.对study进行判断,这无疑是一个很麻烦的事情。将上面的代码用if语句翻译后就成了如下这种形式的代码:

fun doStudy(study: Study?) {
    if (study != null) {
        study.readBooks()
    }
    if (syudy != null) {
        study.doHomework()
    }
}

本来我们只需要通过一次if判空操作,就可以在该if判断内调用study对象的任何方法:

    if (syudy != null) {
   	    study.readBooks()
        study.doHomework()
        · · ·
    }

但是使用了判空辅助工具后我们每次调用方法都需要加上?.来判断,这真的很糟糕!通过let标准函数我们就可以对上面的代码进行优化了,接下来我们学习一下let标准函数的第二个用法:

fun doStudy(study: Study?) {
    study?.let {
        it.readBooks()
        it.doHomework()
    }
}

在上面的②号例子中,我们先通过判空辅助工具?.来判断study是否为null,若不为null的话才会调用let标准函数。在调用let函数后会将study作为参数传入Lambda表达式中此时的study一定不为null。这时上述代码等同于如下代码:

fun doStudy(study: Study?) {
    study?.let {study ->
        study.readBooks()
        study.doHomework()
    }
}

在之前我们学习《Kotlin基础——Lambda表达式》的时候就知道当Lambda表达式只有一个参数时,可以不用声明参数名,而是直接使用it关键字替代参数。此时上述代码变成了:

//final codefun doStudy(study: Study?) {  
    study?.let {  
        it.readBooks()  
        it.doHomework()  
    }  
}

还有一点你需要注意,let函数可以处理全局变量的判空问题,但if判断语句却无法做到这一点。例如下面的代码就无法通过编译,编译器会提示study有空指针风险:

//全局变量
var study: Study? =null

fun doStudy(study: Study?) {  
    if (study != null) {
        //study报错  
        study.readBooks()  
        study.doHomework()  
    }  
}

报错的原因也很简单,因为全局变量的值随时都有可能被其他线程所修改,即使通过if语句进行判空操作,但仍然无法保证if语句中的study变量没有空指针风险。例如在study调用readBooks() 方法后它的值被另一个线程修改为null,此时study再次调用doHomework()时会发生空指针异常的。

1.2 标准函数with

with标准函数接收两个参数:①任何类型的对象Lambda表达式。with函数会将参数作为Lambda表达式中的上下文,并使用Lambda表达式的最后一行代码作为返回值返回。我们来看一段示例代码:

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

输出->
Start eating fruits.
Apple
Banana
Orange
Pear
Grape
Ate all fruits.

上面这段代码很简单,就是用来构建吃水果字符串的。观察上述代码你会发现我们连续调用了很多次builder对象的方法。这种场景下我们通过with函数会让代码更简洁:

val myList = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
val result = with(StringBuilder()) {
    append("Start eating fruits.\n")
    for (fruit in myList) {
        append(fruit).append("\n")
    }
    append("Ate all fruits.")
    //with函数将StringBuilder.toString()作为返回值保存在resule中
    toString()
}
println(result)

我们给with标准函数传入一个StringBuilder对象,然后整个Lambda表达式的上下文都会是这个StringBuilder对象。于是我们就不用在Lambda表达式中再通过myBuilder.append()和myBuilder.toString()这种方式去调用了,而是可以直接使用append()和toString()。最后,with函数会将Lambda表达式的最后一行当作with函数的返回值

1.3 标准函数run

run函数的使用场景和with函数的使用场景很像,可千万不要把他们搞混了。首先run函数不像with函数可以直接使用,而是像let函数一样需要在某个对象的基础上进行调用。其次,run函数只接收一个Lambda表达式作为参数,并且会在Lambda表达式中提供调用对象作为Lambda表达式的上下文,然后同样将Lambda表达式中的最后一行代码作为返回值返回。接下来我们看一个代码示例:

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

输出->
Start eating fruits.
Apple
Banana
Orange
Pear
Grape
Ate all fruits.

总之run函数和with函数的变化非常小,可以用下面一小段代码表示:

//with函数是将对象作为参数
val result = with(StringBuilder()) {
}

//run函数是通过对象进行调用的
val result = StringBuilder().run {
}

1.4 标准函数apply

apply函数跟with函数和run函数也是很类似的~其实我并不是很能理解为什么要设计这么多容易混淆的函数。apply函数也是要通过某个对象来调用的,并且也只接收一个Lambda表达式作为参数,也会将调用apply函数的对象作为Lambda表达式的上下文。看到这你可能要崩溃了,这不是跟run函数一样的吗?但是它跟run函数不一样的地方在于apply函数无法指定返回值,而是会自动返回调用对象本身

val myList = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
val result = StringBuilder().apply {
    append("Start eating fruits.\n")
    for (fruit in myList) {
        append(fruit).append("\n")
    }
    append("Ate all fruits.")
    //自动返回一个StringBuilder对象 然后被保存在result中
}
println(result.toString())

输出->
Start eating fruits.
Apple
Banana
Orange
Pear
Grape
Ate all fruits.

注意这里的变化,apply函数是没有办法指定返回值的,只能返回调用apply对象本身。所以这里的result对象其实是一个StringBuilder对象,我们打印的时候时需要通过StringBuilder.toString()方法才行。

二、静态方法

在Java语言中定义一个静态方法非常简单,只需要在方法上声明一个static关键字就可以了:

public class Util {
    public static void doAction() {
        System.out.println("doAction()");
    }
}

在Java语言中调用静态方法也很简单,直接通过类名调用就可以了:

Util.doAction();

由于静态方法的定义和使用十分简单,所以一般被用来编写工具类。这因为工具类通常不需要创建对象,基本是全局通用的。不过“静态”是Java语言的概念,Kotlin语言本身并不支持静态方法,而是推荐通过单例类的方式来实现:

Kotlin语言规定,所有定义在companion object { }中的方法都可以使用类似于Java静态方法的形式进行调用

2.1 静态方法的定义

接下来我们学习如何在Kotlin语言中实现上述的工具类:

//单例类
object Util {
    fun doSomething1() {
        println("do something1.")
    }
     fun doSomething2() {
        println("do something2.")
    }
}

在上面的代码中,我们通过object关键字定义了一个单例类Util。这个单例类中的方法在所有地方都可以通过类名来访问,例如Util.doSomething1()和Util.doSomething2。虽然这里的两个方法不是静态方法,但是我们仍然可以像Java中那样通过类名的方式直接来访问。
单例类的写法也有一个弊端,就是单例类中的所有方法都会变成类似静态方法的调用方式。如果我们只希望让类中某一个方法变成静态方法的调用方式该怎么办呢?这就需要通过companion object来帮我们实现了:

class Util {
	//必须先创建Util对象才能调用
    fun doAction1() {
        println("do action1")
    }

    companion object {
    	//直接通过Util.doAction2()调用
        fun doAction2() {
            println("do action2")
        }
    }
}

通过companion object关键字,上面的doAction1()和doAction2()两个方法就有了本质的区别。doAction1()必须要先创建一个Util对象才能调用,而doAction2()可以在任何地方直接通过Util.doAction2()的方式进行调用

2.2 伴生类对象

在上面的例子中你已经知道了,在一个类中如果我们想让某个方法以类似静态方法的方式去调用的话,我们可以将这个方法声明在companion object关键字中。虽然doAction2()看起来很像静态方法,用起来也很像静态方法,但它实际上并不是静态方法,而是通过伴生类对象实现的。

class MyClass {  
    companion object {  
        fun doAction() {  
            println("do action.")  
        }  
    }  
}

在这个例子中,MyClass类会有一个名为MyClass.Companion的伴生对象。这个对象包含一个名为doAction的方法,这个方法可以直接通过类名来访问,而不需要创建MyClass的实例。伴生这个概念在Kotlin中用于将与某个类相关联的变量和方法放在一个单例对象中,实现类似于Java中静态成员的功能

2.3 JvmStatic注解

如果你确实想要定义一个真正的静态方法,Kotlin提供给我们两种实现方式:①注解②顶层方法。我们先来学习一下注解的方式吧!

class Util {
    fun doAction1() {
        println("do action1")
    }

    companion object {
        @JvmStatic
        fun doAction2() {
            println("do action2")
        }
    }
}

Kotlin允许我们通过@JvmStatic注解的方式将companion object结构中的方法声明为真正的静态方法。当Kotlin编译器识别到@JvmStatic注解的方法时,就会将这些方法编译成真正的静态方法。

需要注意的是:@JvmStatic注解只能加在单例类或者companion object结构中的方法上

2.4 顶层方法

顶层方法在我们的第9篇文章中有介绍过,我们再来补充一下。在Kotlin中,顶层方法是指直接定义在文件中的方法,而不是定义在类、接口或对象中的方法。顶层方法可以直接使用无需创建对象

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

如果想要定义一个顶层方法,你需要右击包名——>New——>Kotlin File/Class,注意这里我们直接创建的是Kotlin File而不是Class:
在这里插入图片描述
这时候你的包路径下就多了一个MyClass.kt文件,现在直接在该文件中定义的任何方法都会是顶层方法。例如我们在该文件内定义一个doSomething()方法,这就是一个顶层方法:

package com.example.myuipractice

fun doSomething() {
    println("do something···")
}

这样,Kotlin编译器就会在编译的时候将doSomething()方法编译成静态方法了。顶层方法的使用也很简单,我们可以在任何位置直接调用顶层方法,无需通过类名、无需声明对象:
在这里插入图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值