📜  Kotlin 中的局部函数

📅  最后修改于: 2022-05-13 01:54:51.557000             🧑  作者: Mango

Kotlin 中的局部函数

函数背后的想法很简单:将一个大程序分成更小的块,这样可以更容易地推理,并允许重用代码以避免重复。第二点被称为 DRY 原则:不要重复自己。编写相同代码的次数越多,产生错误的可能性就越大。当这个原则得到其合乎逻辑的结论时,您将创建一个由许多小函数组成的程序,每个小函数执行一个单一的东西;这类似于 Unix 的小程序原则,每个程序只做一项工作。同样的原则也适用于函数内部的代码。通常,在Java中,可能会通过调用在同一类或包含静态方法的辅助类中声明的多个支持函数来分解大型函数或方法。

例子

Kotlin 允许我们通过支持在其他函数中声明的函数更进一步。这些被称为局部或嵌套函数。函数甚至可以嵌套多次。打印区域的例子可以写成以下样式:

Kotlin
fun printArea(width: Int, height: Int): Unit {
  fun calculateArea(width: Int, height: Int): Int = width * height
  val area = calculateArea(width, height)
  println("The area is $area")
}


Kotlin
fun printArea2(width: Int, height: Int): Unit {
  fun calculateArea(): Int = width * height
  val area = calculateArea()
  println("The area is $area")
}


Kotlin
fun fizzbuzz(start: Int, end: Int): Unit {
  for (k in start..end) {
    if (k % 3 == 0 && k % 5 == 0)
        println("Fizz Buzz")
    else if (k % 3 == 0)
        println("Fizz")
    else if (k % 5 == 0)
        println("Buzz")
    else
        println(k)
  }
}


Kotlin
fun fizzbuzz2(start: Int, end: Int): Unit {
  fun isFizz(k: Int): Boolean = k % 3 == 0
  fun isBuzz(k: Int): Boolean = k % 5 == 0
  for (k in start..end) {
    if (isFizz(k) && isBuzz(k))
        println("Fizz Buzz")
    else if (isFizz(k))
        println("Fizz")
    else if (isBuzz(k))
        println("Buzz")
    else
        println(k)
  }
}


Kotlin
fun fizzbuzz3(start: Int, end: Int): Unit {
  for (k in start..end) {
    fun isFizz(): Boolean = k % 3 == 0
    fun isBuzz(): Boolean = k % 5 == 0
      
    if (isFizz() && isBuzz())
        println("Fizz Buzz")
    else if (isFizz())
        println("Fizz")
    else if (isBuzz())
        println("Buzz")
    else
        println(k)
  }
}


Kotlin
fun fizzbuzz4(start: Int, end: Int): Unit {
    for (k in start..end) {
      fun isFizz(): Boolean = k % 3 == 0
      fun isBuzz(): Boolean = k % 5 == 0
    when {
      isFizz() && isBuzz() -> println("Fizz Buzz")
      isFizz() -> println("Fizz")
      isBuzz() -> println("Buzz")
      else -> println(k)
      }
  }
}


如您所见, calculateArea函数现在位于printArea内部,因此外部代码无法访问。当我们想要隐藏仅用作较大函数的实现细节的函数时,这很有用。我们也可以通过将成员函数定义为私有来实现类似的效果。那么本地函数还有其他优势吗?是的,他们做到了!局部函数可以访问外部作用域中定义的参数和变量:

科特林

fun printArea2(width: Int, height: Int): Unit {
  fun calculateArea(): Int = width * height
  val area = calculateArea()
  println("The area is $area")
}

请注意,我们已经从calculateArea函数中删除了参数,现在它直接使用在封闭范围中定义的参数。这使得嵌套函数更具可读性并节省了重复参数定义,这对于具有许多参数的函数非常有用。让我们看一个可以使用局部函数分解的函数示例:

科特林

fun fizzbuzz(start: Int, end: Int): Unit {
  for (k in start..end) {
    if (k % 3 == 0 && k % 5 == 0)
        println("Fizz Buzz")
    else if (k % 3 == 0)
        println("Fizz")
    else if (k % 5 == 0)
        println("Buzz")
    else
        println(k)
  }
}

这就是著名的 Fizz Buzz 问题。该要求要求您打印出从开始值到结束值的整数。但是,如果整数是 3 的倍数,则应打印 Fizz。如果它是 5 的倍数,则应打印 Buzz。如果是 3 和 5 的倍数,则一起打印 Fizz Buzz。

第一个解决方案简短易读,但它重复了一些代码。模检查被编码两次,这使错误的可能性加倍。显然,这个例子非常简单,因此出现拼写错误的可能性很小;但是,它用于证明更大问题的问题。我们可以为每个模检查声明一个本地函数,这样我们只需要编写一次。这将我们带到了解决方案的下一个迭代:

科特林

fun fizzbuzz2(start: Int, end: Int): Unit {
  fun isFizz(k: Int): Boolean = k % 3 == 0
  fun isBuzz(k: Int): Boolean = k % 5 == 0
  for (k in start..end) {
    if (isFizz(k) && isBuzz(k))
        println("Fizz Buzz")
    else if (isFizz(k))
        println("Fizz")
    else if (isBuzz(k))
        println("Buzz")
    else
        println(k)
  }
}

在这里,我们的 if...else 分支现在调用嵌套函数 isFizz 和 isBuzz。但是,每次将 k 传递给函数仍然有点冗长。我们有办法避免这种情况吗?事实证明,答案是肯定的!我们不仅可以直接在其他函数内部定义局部函数,还可以在 for 循环、while 循环和其他块中定义局部函数:

科特林

fun fizzbuzz3(start: Int, end: Int): Unit {
  for (k in start..end) {
    fun isFizz(): Boolean = k % 3 == 0
    fun isBuzz(): Boolean = k % 5 == 0
      
    if (isFizz() && isBuzz())
        println("Fizz Buzz")
    else if (isFizz())
        println("Fizz")
    else if (isBuzz())
        println("Buzz")
    else
        println(k)
  }
}

在函数的第三次迭代中,我们将函数定义移到了 for 循环中。所以现在,我们可以省略参数声明,直接访问k。最后,我们可以利用 Kotlin 基础知识来消除 if...else 关键字的一些干扰:

科特林

fun fizzbuzz4(start: Int, end: Int): Unit {
    for (k in start..end) {
      fun isFizz(): Boolean = k % 3 == 0
      fun isBuzz(): Boolean = k % 5 == 0
    when {
      isFizz() && isBuzz() -> println("Fizz Buzz")
      isFizz() -> println("Fizz")
      isBuzz() -> println("Buzz")
      else -> println(k)
      }
  }
}

这给了我们最终的解决方案,它避免了代码的重复,并且比初始迭代更具可读性。