Kotlin中Lambda表达式中的默认参数

时间:2019-10-07 21:37:57

标签: kotlin lambda

我想创建一个lambda并将其分配给变量,并且可以按预期进行以下工作:

val rollDice = { min: Int, max: Int -> (min..max).random() }

但是,当我尝试为参数分配默认值时,出现错误:

val rollDice = { min: Int = 1, max: Int = 12 -> (min..max).random() }
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Unexpected tokens (use ';' to separate expressions on the same line)

是否无法在Kotlin中为lambda表达式中的参数分配默认值?

1 个答案:

答案 0 :(得分:4)

TLDR: Lambda表达式不能具有默认参数。如果需要它们,则应声明一个函数(可以在另一个函数内部)。


详细说明一下,让我们看看如何在Kotlin中定义类似函数的类型。凭直觉,人们希望它们能够相同地工作,但是它们的功能之间存在细微的差异。

1。重载功能

在手动定义函数重载时(以Java方式),不仅可以使用任何允许的参数号调用函数,而且还可以将函数引用存储使用任何参数数字键入。

fun overload(min: Int, max: Int) = (min..max).random()
fun overload(min: Int) = overload(min, 12)
fun overload() = overload(1, 12)

// Calling is possible with all numbers of arguments, and naming ones at the end
overload()
overload(3)
overload(min=3)
overload(3, 4)
overload(3, max=4)
overload(min=3, max=4)

// Intuitively, all 3 ways of storing work:
val f: (Int, Int) -> Int = ::overload
val g: (Int) -> Int = ::overload
val h: () -> Int = ::overload

// On the other hand, this does NOT compile because of ambiguity:
val i = ::overload

2。具有默认参数的功能

在Kotlin中,更惯用的是使用默认参数。虽然这似乎主要等同于重载函数,但事实并非如此。显着的区别是:仅声明一个函数,并且类型推断仅在调用时才考虑不同的参数计数,而在通过函数引用进行 storing 时则不考虑。 / p>

fun default(min: Int = 1, max: Int = 12) = (min..max).random()

// Calling is possible exactly like overloaded functions
default()
default(3)
default(min=3)
default(3, 4)
default(3, max=4)
default(min=3, max=4)

// No ambiguity, f and g have the same type (all parameters)
val f = ::default
val g: (Int, Int) -> Int = ::default

// However, storing in a function type taking fewer arguments is NOT possible
val h: (Int) -> Int = ::default
val i: () -> Int = ::default

3。匿名函数

匿名函数甚至在声明中也不允许使用默认参数,因此只有一种调用它们的方法。此外,存储它们的变量属于函数类型,它会丢失有关参数名称的信息,从而阻止使用命名参数进行调用。

val anonymous = fun(min: Int, max: Int) = (min..max).random()
val anonymous: (Int, Int) -> Int = fun(min: Int, max: Int) = (min..max).random()

// Only one way to call
anonymous(3, 4)

// No ambiguity, f and g have the same (full type)
val f = anonymous
val g: (Int, Int) -> Int = anonymous

// Mistake, which compiles: this declares h as a *property*,
// with type KProperty<(Int, Int) -> Int>
val h = ::anonymous

// Calling with named arguments is NOT possible
anonymous(3, 4)         // OK
anonymous(min=3, max=4) // error

4。 Lambda表达式

与匿名函数一样,lambda表达式不允许使用默认参数,也不能使用命名参数进行调用。由于它们以(Int, Int) -> Int之类的函数类型立即存储,因此它们受到与引用实际函数的函数类型相同的限制。

类型推断仅在参数类型在lambda表达式中或在要分配给的函数类型中指定的情况下有效:

// OK:
val lambda = { min: Int, max: Int -> (min..max).random() }
val lambda2: (Int, Int) -> Int = { min, max -> (min..max).random() }

// Type inference fails:
val lambda3 = { min, max -> (min..max).random() }

这里的主要要点是,这4个可调用对象虽然支持相同的基本功能,但在以下几点上有所不同:

  • 允许声明和调用默认参数
  • 允许通过考虑默认参数的功能引用进行存储
  • 允许使用命名参数进行调用

通过将可调用对象称为函数类型(这是匿名函数和lambda的唯一选项),您将丢失原始声明中存在的信息。