我想创建一个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)

Is it not possible to assign default values to parameters in lambda expressions in Kotlin?

推荐答案

TLDR: Lambda expressions cannot have default parameters. If you need them, you should declare a function (can be locally inside another function).


为了详细说明,让我们看看如何在Kotlin中定义类似函数的类型.从直觉上看,人们会认为它们的工作原理是一样的,但它们的功能存在细微的差异.

1. Overloaded functions

When defining function overloads manually (the Java way), it's possible to not just call the function with any allowed argument number, but also store the function reference in a type using any argument number.

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. Functions with default parameters

More idiomatic in Kotlin is the use of default parameters. While this seems to be mostly equivalent to overloaded functions, it's not. The notable difference is: only a single function is declared, and type inference will consider different argument counts only when calling the function, but not when storing it via function reference.

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. Anonymous functions

Anonymous functions allow no default parameters even in the declaration, so there's only one way of calling them. Furthermore, the variable storing them is of function type, which loses information about the parameter names and thus prevents a call with named arguments.

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 expressions

Like anonymous functions, lambda expressions allow no default parameters and cannot be called with named arguments. Since they are stored immediately as a function type like (Int, Int) -> Int, they undergo the same restrictions as function types referring to actual functions.

只有在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 declaration and calling of default parameters
  • 允许通过考虑默认参数的函数引用执行storing
  • Allows calling with named arguments

By referring to the callables as function types (which is the only option for anonymous functions and lambdas), you lose information that's present in the original declaration.

Kotlin相关问答推荐

在Kotlin 有更好的结合方式?

如何在不基于数据 map 的情况下将图例添加到lets plot kotlin

在Spring Boot应用程序中使用网络请求功能将关键字挂起作为冗余

在 Kotlin 中,为什么我们要在闭包中捕获值

如何从 Period.between() 返回的字符串中提取信息? (Kotlin )

Kotlin 中的 maxOf() 和 max() 方法有什么区别?

在 kotlin 原始字符串中转义三重引号

kotest 更改环境变量

如何使用 Kotlin KClass 属性 simpleName 生成空值

Fragment的onDestroy()中是否需要将ViewBinding设置为null?

Koin Android:org.koin.error.NoBeanDefFoundException

防止导航到同一个片段

为什么 Kotlin Pair 中的条目不可变?

Kotlin:sealed class cannot "contain" data classes?

在Kotlin中使用@Service时引发异常

(kotlin的Moshi)@Json vs@field:Json

Kotlin 的数据类 == C# 的 struct ?

Java的Kotlin:字段是否可以为空?

从 Kotlin 访问 Integer.class

Kotlin中对象和数据类的区别是什么?