Kotlin:标准函数和静态方法

本文详细介绍了Kotlin中的标准函数,如run、let、also、with、apply等,及其用法和区别。同时,讨论了Kotlin如何通过单例类和companion object实现类似静态方法的功能。此外,还解释了如何通过@JvmStatic注解和顶层函数来定义真正的静态方法,以满足Java互操作的需求。
摘要由CSDN通过智能技术生成

Kotlin:标准函数和静态方法

1 标准函数

标准函数是指在Standard.kt中的函数,任何Kotlin代码都可以自由调用所有的标准函数。 以下是Standard.kt的源码:

@kotlin.internal.InlineOnly
public inline fun <R> run(block: () -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}

@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}

@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block(this)
    return this
}

@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}

@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? {
    contract {
        callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
    }
    return if (predicate(this)) this else null
}

@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? {
    contract {
        callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
    }
    return if (!predicate(this)) this else null
}

@kotlin.internal.InlineOnly
public inline fun repeat(times: Int, action: (Int) -> Unit) {
    contract { callsInPlace(action) }

    for (index in 0 until times) {
        action(index)
    }
}

以下是标准函数的使用:

data class Student(val name: String, var grade: Int)

fun check(student: Student?) {
    val result1 = student?.let {
        it.grade = 6
        100
    }
    println("student = $student") // student = Student(name=小E, grade=6)
    println("result1 = $result1") // result1 = 100

    val result2 = student?.also {
        it.grade = 7
        100
    }
    println("student = $student") // student = Student(name=小E, grade=7)
    println("result2 = $result2") // result2 = Student(name=小E, grade=7)

    val result3 = with(student) {
        this?.grade = 8
        100
    }
    println("student = $student") // student = Student(name=小E, grade=8)
    println("result3 = $result3") // result3 = 100

    val result4 = student?.run {
        grade = 9
        100
    }
    println("student = $student") // student = Student(name=小E, grade=9)
    println("result4 = $result4") // result4 = 100

    val result5 = student?.apply {
        grade = 11
        100
    }
    println("student = $student") // student = Student(name=小E, grade=11)
    println("result4 = $result5") // result4 = Student(name=小E, grade=11)
}

fun main() {
    val student = Student("小E", 5)
    check(student)
}
  • let函数能使某个变量作用于Lambda表达式里,让it关键字引用变量,返回Lambda的最后一行;
  • also函数能使某个变量作用于Lambda表达式里,让it关键字引用变量,返回对象本身;
  • with函数接收两个参数,第一个参数可以是一个任意类型的对象,第二个参数是一个Lambda表达式。with函数会在Lambda表达式中提供第一个参数对象的上下文,并使用Lambda表达式中的最后一行代码作为返回值返回;
  • run函数接收一个Lambda参数,并且会在Lambda表达式中提供调用对象的上下文。其他方面和with函数是一样的,包括也会使用Lambda表达式中的最后一行代码作为返回值返回。
  • apply函数接收一个Lambda参数,也会在Lambda表达式中提供调用对象的上下文,但是apply函数无法指定返回值,而是会自动返回调用对象本身。

总结:

  • letwithrun最后一行是返回值,alsoapply返回的是对象;
  • letalso代码块中用it来代表对象(也可以自定义),其它的用this表示,也可以省略;

2 定义静态方法

静态方法在某些编程语言里面又叫作类方法,指的就是那种不需要创建实例就能调用的方法,所有主流的编程语言都会支持静态方法这个特性。

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

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

这是一个非常简单的工具类,上述代码中的doAction()方法就是一个静态方法。调用静态方法并不需要创建类的实例,而是可以直接以Util.doAction()这种写法来调用。因而静态方法非常适合用于编写一些工具类的功能,因为工具类通常没有创建实例的必要,基本是全局通用的。

但是和绝大多数主流编程语言不同的是,Kotlin却极度弱化了静态方法这个概念,想要在Kotlin中定义一个静态方法反倒不是一件容易的事。那么Kotlin为什么要这样设计呢?因为Kotlin提供了比静态方法更好用的语法特性——单例类。

像工具类这种功能,在Kotlin中就非常推荐使用单例类的方式来实现,比如上述的Util工具类,如果使用Kotlin来实现的话就可以这样写:

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

虽然这里的doAction()方法并不是静态方法,但是仍然可以使用Util.doAction()的方式来调用,这就是单例类所带来的便利性。

不过,使用单例类的写法会将整个类中的所有方法全部变成类似于静态方法的调用方式,**如果只是希望让类中的某一个方法变成静态方法的调用方式该怎么办呢?这个时候就可以使用companion object了,**示例如下:

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

这里首先将Util从单例类改成了一个普通类,然后在类中直接定义了一个doAction1()方法,又在companion object中定义了一个doAction2()方法。现在这两个方法就有了本质的区别,因为doAction1()方法是一定要先创建Util类的实例才能调用的,而 doAction2()方法可以直接使用Util.doAction2()的方式调用。

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

由此可以看出,Kotlin确实没有直接定义静态方法的关键字,但是提供了一些语法特性来支持类似于静态方法调用的写法,这些语法特性基本可以满足我们平时的开发需求了。

如果你确实需要定义真正的静态方法,Kotlin仍然提供了两种实现方式:注解和顶层方法。

先来看注解,前面使用的单例类和companion object都只是在语法的形式上模仿了静态方法的调用方式,实际上它们都不是真正的静态方法。因此如果你在Java代码中以静态方法的形式去调用的话,你会发现这些方法并不存在。**而如果给单例类或companion object中的方法加上@JvmStatic注解,那么Kotlin编译器就会将这些方法编译成真正的静态方法,**如下所示:

class Utils {

    fun doAction1() { }

    companion object {
        @JvmStatic
        fun doAction2() { }
    }

}

注意,@JvmStatic注解只能加在单例类或companion object中的方法上,如果加在 一个普通方法上,会直接提示语法错误。

由于doAction2()方法已经成为了真正的静态方法,那么现在不管是在Kotlin中还是在Java中,都可以使用Util.doAction2()的写法来调用了。

再来看顶层方法,顶层方法指的是那些没有定义在任何类中的方法,Kotlin编译器会将所有的顶层方法全部编译成静态方法,因此只要你定义了一 个顶层方法,那么它就一定是静态方法。

想要定义一个顶层方法,首先需要创建一个Kotlin文件。任意包名右击 -> New -> Kotlin File/Class,在弹出的对话框中输入文件名即可,注意创建类型要选择File,如图所示:

创建Kotlin文件

在这个文件 中定义的任何方法都会是顶层方法,比如定义一个doSomething()方法,如下所示:

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

Kotlin编译器会将所有的顶层方法全部编译成静态方法。如果是在Kotlin代码中调用的话,所有的顶层方法都可以在任何位置被直接调 用,不用管包名路径,也不用创建实例, 直接键入doSomething()即可,如图所示:

调用顶层方法

但如果是在Java代码中调用,是找不到doSomething()这个方法的,因为Java中没有顶层方法这个概念,所有的方法必须定义在类中。对于Helper.kt这个类,Kotlin编译器会自动创建一个叫作HelperKtJava类,doSomething()方法就是以静态方法的形式定义在HelperKt类里面的, 因此在Java中使用HelperKt.doSomething()的写法来调用就可以了,如图所示:

在Java代码中调用doSomething方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值