3.2.1 演化带有默认参数的函数

快速浏览一下我们之前编写的greet()函数,为了方便起见,这里再重复一遍。

greet()函数有一个硬编码字符串"Hello",但是如果我们希望为函数的调用方提供灵活性,以便提供它所选择的有趣内容,该怎么办呢?

如果我们向函数中添加了一个新参数,那么调用该函数的任何现有代码都将中断,因为它们将缺少急需的(尽管是新的)参数。在像Java这样的语言中,我们为此使用了重载,但这可能会导致代码重复。Kotlin通过使用默认参数简化了这项任务。

默认参数是在声明之后接受默认值的参数。如果调用方没有传递该参数的值,则使用默认值。指定参数的名称、冒号、类型,后跟=对默认值进行赋值。让我们将greet方法改为接受一个额外的参数,但使用默认值。

只使用一个name参数调用greet()的现有代码可以继续工作。任何对greet()的新调用都可以传递一个参数(name),或者两个参数(name和msg)。在第一个调用中,由于没有提供msg的值,因此使用了默认参数Hello。在第二个调用中,使用了给定参数Howdy,忽略了默认参数。

在greet()中,具有默认参数的参数放置在常规参数(即没有默认参数的参数)之后。你可能想知道调换它们出现的顺序是否也可以。是的,可以,但后果是:

□由于常规参数需要一个值,调用方将被迫为具有默认参数的参数提供一个值——这就违背了使用默认参数的目的。

□如果调用方使用命名参数,则可以跳过默认参数——将在3.2.2节中看到这一点。

□具有默认参数的参数可以放在表示lambda表达式的最后一个参数之前——我们将在10.2.5节中看到这一点的好处。

简而言之,要使默认参数有效,可以将其用在末尾的参数上,并且可以选择只跟在lambda表达式参数的后面。

默认参数不必是字面量,它可以是表达式。另外,可以使用参数左边的参数计算参数的默认参数。

greet()函数中使用的默认参数有一个硬编码值。让我们把它改为使用其左边的参数:

同样,当两个参数传递给greet()时,默认参数将被忽略,它不会被计算。另一方面,如果只传递name参数的值,则从默认参数表达式计算msg参数的值。在对greet()的第二个调用中,只传递名字和结果。msg是根据name参数的值计算出来的——和好朋友Scott击掌庆祝一下。

在本例中,交换name和msg的位置将会导致编译错误,即name在默认参数表达式中未初始化——这是将具有默认参数的参数放在末尾位置上的另一个原因。