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.