以下示例在Kotlin 1.3.21中是完全合法的:
fun <T> foo(bar: T): T = bar
val t: Int = foo(1) // No need to declare foo<Int>(1) explicitly
Run Code Online (Sandbox Code Playgroud)
但是为什么类型推断不能用于高阶函数呢?
fun <T> foo() = fun(bar: T): T = bar
val t: Int = foo()(1) // Compile error: Type inference failed...
Run Code Online (Sandbox Code Playgroud)
使用高阶函数时,Kotlin会强制呼叫站点为:
val t = foo<Int>()(1)
Run Code Online (Sandbox Code Playgroud)
即使foo明确指定了返回类型,类型推断仍然会失败:
fun <T> foo(): (T) -> T = fun(bar: T): T = bar
val t: Int = foo()(1) // Compile error: Type inference failed...
Run Code Online (Sandbox Code Playgroud)
但是,当泛型类型参数与外部函数共享时,它将起作用!
fun <T> foo(baz: T) = fun (bar: T): T = bar
val t: Int = foo(1)(1) // Horray! But I want to write foo()(1) instead...
Run Code Online (Sandbox Code Playgroud)
如何编写函数foo以进行foo()(1)编译,bar泛型类型在哪里?
小智 6
I am not an expert on how type inference works, but the basic rule is: At the point of use the compiler must know all types in the expression being used.
因此,根据我的理解是:
foo()<-在此处使用类型信息
foo()(1)<-在此处提供信息
看起来类型推断不能“向后”工作
val foo = foo<Int>()//create function
val bar = foo(1)//call function
Run Code Online (Sandbox Code Playgroud)
简单地说(可能过于简单化),当你调用动态生成的函数时,例如高阶函数的返回值,它实际上并不是函数调用,它只是函数的语法糖invoke。
在语法级别,Kotlin 将返回类型的对象视为() -> A普通(A, B) -> C函数 - 它允许您通过在括号中附加参数来调用它们。这就是为什么你可以这样做foo<Int>()(1)-foo<Int>()返回一个类型的对象,然后将其作为参数(Int) -> (Int)调用。1
然而,在幕后,这些“函数对象”并不是真正的函数,它们只是带有invoke运算符方法的普通对象。例如,接受 1 个参数并返回一个值的函数对象实际上只是特殊接口的实例Function1,看起来像这样
interface Function1<A, R> {
operator fun invoke(a: A): R
}
Run Code Online (Sandbox Code Playgroud)
任何带有 的类都operator fun invoke可以像函数一样调用,即foo.invoke(bar, baz)您可以直接调用foo(bar, baz)。Kotlin 有几个内置类,例如Function、Function1、Function2等Function<number of args>,用于表示函数对象。所以当你打电话时foo<Int>()(1),你实际打电话的是foo<Int>().invoke(1)。您可以通过反编译字节码来确认这一点。
那么这与类型推断有什么关系呢?好吧,当您调用 时foo()(1),您实际上是在调用foo().invoke(1)一些语法糖,这使得更容易理解推理失败的原因。点运算符的右侧不能用于推断左侧的类型,因为必须首先评估左侧。因此 的类型foo必须明确声明为foo<Int>。
| 归档时间: |
|
| 查看次数: |
304 次 |
| 最近记录: |