Kotlin 中 lambda 表达式的默认参数

2024-01-11

我想创建一个 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 表达式中的参数分配默认值?


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


为了详细说明,让我们看看如何在 Kotlin 中定义类函数类型的不同方式。直觉上,人们会期望它们的工作原理相同,但它们的功能存在细微的差异。

1. 函数重载

当手动定义函数重载(Java 方式)时,可能不仅仅是call具有任何允许的参数编号的函数,而且store使用任意参数编号的类型中的函数引用。

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 中更惯用的做法是使用默认参数。虽然这看起来基本上等同于重载函数,但事实并非如此。显着的区别是:仅声明一个函数,并且类型推断仅在以下情况下才会考虑不同的参数计数:calling函数,但不是何时storing它通过函数引用。

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 个可调用对象虽然支持相同的基本功能,但存在以下几点不同:

  • Allows 声明和调用默认参数
  • Allows storing通过考虑默认参数的函数引用
  • 允许使用命名参数调用

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Kotlin 中 lambda 表达式的默认参数 的相关文章

随机推荐