四、Android-Kotlin函数

146 阅读3分钟

4. Kotlin-函数

4.1 with函数

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

        val with = with(obj) {
            // 这里是 obj 的上下文
            "value" //with 函数的返回值
        }
        val listOf = listOf<String>("Apple", "Banana", "Orange", "Pear", "Grape")
        val obj = StringBuffer()
        obj.append("Start eating fruits.\n")
        for (s in listOf) {
            obj.append(s).append("\n")
        }
        obj.append("Ate all fruits.")
        val result = obj.toString()
        println(result)
        val listOf = listOf<String>("Apple", "Banana", "Orange", "Pear", "Grape")
        val result = with(StringBuffer()) {
 
            append("Start eating fruits.\n")
            for (s in listOf) {
                append(s).append("\n")
            }
            append("Ate all fruits.")
            toString()
        }
        Log.d("TAG", "initData: $result")

4.2 run函数

run函数的用法和使用场景和上面with类似,run函数不会直接调用,而是要在某个对象的基础上调用;run函数只接受一个Lambda函数,并且在Lambda表达式中提供调用对象的上下文。使用Lambda表达式中最后一行代码作为返回值返回。

       val result = obj.run {
            // 这里是obj的上下文
            "value" // run函数的返回值
        }
        val listOf = listOf<String>("Apple", "Banana", "Orange", "Pear", "Grape")
        val result = StringBuffer().run {
 
            append("Start eating fruits.\n")
            for (s in listOf) {
                append(s).append("\n")
            }
            append("Ate all fruits.")
            toString()
 
        }
 
        Log.d("TAG", "initData: $result")

4.3 apply函数

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

          val result = obj.apply {
            // 这里是obj的上下文
        }
        // result == obj
        val listOf = listOf<String>("Apple", "Banana", "Orange", "Pear", "Grape")
        val result = StringBuffer().apply {
 
            append("Start eating fruits.\n")
            for (s in listOf) {
                append(s).append("\n")
            }
            append("Ate all fruits.")
        }
 
        Log.d("TAG", "initData: ${result.toString()}")

4.4 静态方法

静态方法一般又叫做类方法,指的就是不需要创建实例就能调用的方法。

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

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

和绝大多数主流语言不同的是,Kotlin弱化了静态方法的概念,在Kotlin中可以使用单例类(object) 。来达到类名直接调用的效果,但它是属于单例类。

object Util {
    public fun doAction() {
        println("doAction")
    }
}
// 调用
Util.doAction()

但是这会让Util中所有方法都变成类名调用。那可以使用 compaion object 来达到 static 的效果。

class Util {
    companion object {
        fun doAction() {
            println("doAction")
        }
    }
    
    fun doAction2() {
        println("doAction2")
    }
}
// 调用
   Util.doAction()
   val u = Util()
   u.doAction2()

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

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

4.5 真正的静态方法

  • 注解 @JvmStatic

只能用在单例类companion object中。在这之中的方法加上 @JvmStatic 就会被Kotlin编译器编译成真正的静态方法。

class Util {
    companion object {
        @JvmStatic
        fun doAction() {
            println("doAction")
        }
    }
}

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

  • 顶层方法

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

新建一个Kotlin文件,里面直接定义方法。然后可以在任何位置被直接调用。不用管包路径,也不用创建实例。

java中调用需要 文件名Kt 类名调用。

4.6 repeat函数

根据传入的重复次数去重复执行一个我们想要的动作(函数)

repeat(5){ println("我是重复的第${it + 1}次,我的索引为:$it") }

4.7 扩展函数

Kotlin的扩展函数类似于Swift的Extension,Java中没有扩展函数。

语法结构:

fun ClassName.methodName(param1: Int, param2: Int): Int {
    return 0
}

例如给String类添加一个扩展方法

// 计算字符串中字符有多少个
fun String.letterCount(): Int {
    var count = 0
    for (char in this) {
        if (char.isLetter()) {
            count++
        }
    }
    return count
}
// 调用
val count = "123456ABCDEF@#$%^".letterCount()

4.8 运算符重载

语法结构:

class Obj {
    operator fun plus(obj: Obj): Obj {
        // 处理
    }
}

关键字 operator, 函数名也是固定的,plus是重载的+号运算符。

class Money {
    var value: Int = 0
​
    constructor(value: Int) {
        this.value = value
    }
    // 加相同类型
    operator fun plus(money: Money): Money {
        val sum = value + money.value
        return Money(sum)
    }
    // 加不同类型,重载多个plus
    operator fun plus(newValue: Int): Money {
        val sum = value + newValue
        return Money(sum)
    }
}
// 调用
val m1 = Money(5)
val m2 = Money(10)
val m3 = m1 + m2
val m4 = m1 + 20
语法糖表达式实际调用函数
a + ba.plus(b)
a - ba.minus(b)
a * ba.times(b)
a / ba.div(b)
a % ba.rem(b)
a++a.inc()
a--a.dec()
+aa.unaryPlus()
-aa.unaryMinus()
!aa.not()
a == ba.equals(b)
a > ba.compareTo(b)
a < ba.compareTo(b)
a >= ba.compareTo(b)
a <= ba.compareTo(b)
a..ba.rangeTo(b)
a[b]a.get(b)
a[b] = ca.set(b, c)
a in bb.contains(a)

4.9 高阶函数

fun example(func: (String, Int) -> Unit) {
    func("hello", 123)
}

一个函数的参数或者返回值类型是函数类型的话,那这个函数就是高阶函数。

(String, Int) -> Unit就是一个函数类型。这个函数类型表明了接收2个参数,一个String类型,一个Int类型,如果不需要参数,则一对空括号就行;-> 右边是返回值,声明该函数的返回值类型,如果没有返回值则使用Unit,相当于Java中的void

上面的example高阶函数,接收一个函数类型的参数。

使用示例:

fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int {
    val result = operation(num1, num2)
    return result
}
fun plus(num1: Int, num2: Int): Int {
    return num1 + num2
}
fun minus(num1: Int, num2: Int): Int {
    return num1 - num2
}
fun main() {
    val num1 = 100
    val num2 = 80
    // 函数传入方式
    val result1 = num1AndNum2(num1, num2, ::plus)
    val result2 = num1AndNum2(num1, num2, ::minus)
    println("result1 is $result1") // 180
    println("result2 is $result2") // 20
    // Lambda方式
    val r3 = num1AndNum2(num1, num2) { n1, n2 ->
      n1 * n2
    }
    println("result2 is $r3") // 8000
}

第三个参数使用了::plus::minus这种写法。

这是一种函数引用方式的写法,表示将plus()minus()函数作为参数传递给num1AndNum2()函数。

还有一种Lambda写法,可以直接在函数后面跟匿名函数。

4.10 内联函数-inline

inline fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int {
 val result = operation(num1, num2)
 return result
}

工作原理:Kotlin编译器会将内联函数中的代码在编译的时候自动替换到调用它的地方,这样就不存在运行时的开销了。

inline fun inlineTest(block1: () -> Unit, noinline block2: () -> Unit) {
}
  • noinline: 标记Lambda不内联
inline fun runRunnable(crossinline block: () -> Unit) {
 val runnable = Runnable {
 block()
 }
 runnable.run()
}
  • crossinline:我们就无法在调用runRunnable函数时的Lambda表达式中使用return关键字进行函数返回了,但是仍然可以使用return@runRunnable的写法进行局部返回。总体来说,除了在return关键字的使用上有所区别之外,crossinline保留了内联函数的其他所有特性。