Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

命名参数和默认参数

在函数调用过程中,您可以提供参数名称。

命名参数提高了代码的可读性。这对于参数列表很长且复杂的情况尤为重要 - 命名参数可以足够清晰,以至于读者可以在不查看文档的情况下理解函数调用。

在下面的示例中,所有的参数都是 Int 类型。命名参数可以澄清它们的含义:

// NamedAndDefaultArgs/NamedArguments.kt
package color1
import atomictest.eq

fun color(red: Int, green: Int, blue: Int) =
  "($red, $green, $blue)"

fun main() {
  color(1, 2, 3) eq "(1, 2, 3)"   // [1]
  color(
    red = 76,                     // [2]
    green = 89,
    blue = 0
  ) eq "(76, 89, 0)"
  color(52, 34, blue = 0) eq      // [3]
    "(52, 34, 0)"
}
  • [1] 这并没有提供太多信息。您需要查看文档才能知道这些参数的含义。
  • [2] 每个参数的含义都很清晰。
  • [3] 您不需要为所有的参数命名。

命名参数使您可以更改颜色的顺序。在这里,我们首先指定了 blue

// NamedAndDefaultArgs/ArgumentOrder.kt
import color1.color
import atomictest.eq

fun main() {
  color(blue = 0, red = 99, green = 52) eq
    "(99, 52, 0)"
  color(red = 255, 255, 0) eq
    "(255, 255, 0)"
}

您可以混合使用命名参数和常规(位置)参数。如果更改参数的顺序,您应该在整个调用过程中使用命名参数 - 不仅仅是为了可读性,还因为编译器通常需要告诉它们参数的位置。

当与默认参数结合使用时,命名参数尤其有用。默认参数是函数定义中指定的参数的默认值:

// NamedAndDefaultArgs/Color2.kt
package color2
import atomictest.eq

fun color(
  red: Int = 0,
  green: Int = 0,
  blue: Int = 0,
) = "($red, $green, $blue)"

fun main() {
  color(139) eq "(139, 0, 0)"
  color(blue = 139) eq "(0, 0, 139)"
  color(255, 165) eq "(255, 165, 0)"
  color(red = 128, blue = 128) eq
    "(128, 0, 128)"
}

如果不提供任何参数,那么未提供的参数将采用其默认值,因此您只需要提供与默认值不同的参数。如果参数列表很长,这简化了生成的代码,使其更易于编写和阅读。

该示例还在 color() 的定义中使用了 尾随逗号。尾随逗号是最后一个参数(blue)之后的额外逗号。当参数或值写在多行上时,这非常有用。有了尾随逗号,您可以添加新项目并更改它们的顺序,而无需添加或删除逗号。

命名参数和默认参数(以及尾随逗号)在构造函数中同样适用:

// NamedAndDefaultArgs/Color3.kt
package color3
import atomictest.eq

class Color(
  val red: Int = 0,
  val green: Int = 0,
  val blue: Int = 0,
) {
  override fun toString() =
    "($red, $green, $blue)"
}

fun main() {
  Color(red = 77).toString() eq "(77, 0, 0)"
}

joinToString() 是一个使用默认参数的标准库函数。它将可迭代对象(列表、集合或范围)的内容合并为一个 String。您可以指定分隔符、前缀元素和后缀元素:

// NamedAndDefaultArgs/CreateString.kt
import atomictest.eq

fun main() {
  val list = listOf(1, 2, 3,)
  list.toString() eq "[1, 2, 3]"
  list.joinToString() eq "1, 2, 3"
  list.joinToString(prefix = "(",
    postfix = ")") eq "(1, 2, 3)"
  list.joinToString(separator = ":") eq
    "1:2:3"
}

默认情况下,List 的默认 toString() 方法会返回方括号中的内容,这可能不是您想要的。joinToString() 的参数的默认值是用逗号作为 separator,用空字符串作为 prefixpostfix。在上面的示例中,我们使用命名参数和默认参数来指定我们想要更改的参数。

list 的初始化器包含一个尾随逗号。通常,只有在每个元素占据一行的情况下才会使用尾随逗号。

如果将对象用作默认参数,则会为每次调用创建该对象的新实例:

// NamedAndDefaultArgs/Evaluation.kt
package namedanddefault

class DefaultArg

fun h(d: DefaultArg = DefaultArg()) =
  println(d)

fun main() {
  h()
  h()
}
/* Sample output:
DefaultArg@28d93b30
DefaultArg@1b6d3586
*/

h() 的两次调用的 DefaultArg 对象的地址不同,表明存在两个不同的对象。

当提高可读性时,请指定参数名称。比较以下两个对 joinToString() 的调用:

// NamedAndDefaultArgs/CreateString2.kt
import atomictest.eq

fun main() {
  val list = listOf(1, 2, 3)
  list.joinToString(". ", "", "!") eq


    "1. 2. 3!"
  list.joinToString(separator = ". ",
    postfix = "!") eq "1. 2. 3!"
}

如果没有记住参数顺序的话,很难猜测 ". """ 是分隔符。

作为默认参数的另一个示例,trimMargin() 是一个标准库函数,用于格式化多行的 String。它使用边距前缀 String 来确定每行的开头。trimMargin() 会从源 String 的每一行中去除前导空白字符和边距前缀。如果第一行和最后一行是空行,则它会删除它们:

// NamedAndDefaultArgs/TrimMargin.kt
import atomictest.eq

fun main() {
  val poem = """
    |->Last night I saw upon the stair
        |->A little man who wasn't there
          |->He wasn't there again today
|->Oh, how I wish he'd go away."""
  poem.trimMargin() eq
"""->Last night I saw upon the stair
->A little man who wasn't there
->He wasn't there again today
->Oh, how I wish he'd go away."""
  poem.trimMargin(marginPrefix = "|->") eq
"""Last night I saw upon the stair
A little man who wasn't there
He wasn't there again today
Oh, how I wish he'd go away."""
}

|(“pipe”)是边距前缀的默认参数,您可以将其替换为所选的 String

练习和解答可以在 www.AtomicKotlin.com 上找到。