Kotlin如何调度invoke运算符?

bre*_*dan 10 kotlin

Kotlin如何消除函数调用,构造函数,伴随对象和调用重载的歧义?在Kotlin 1.3.11,我可以在同一范围内声明两个同名成员:

fun main(args: Array<String>) {
    val test = object {
        operator fun invoke() = println("test invocation")
    }

    test() // Prints: "test invocation"

    // I think this should fail to compile, but it works
    fun test() = println("test function")

    test() // Prints: "test function"
}
Run Code Online (Sandbox Code Playgroud)

您可能认为它使用了最新的声明,但并非如此!

fun main(args: Array<String>) {
    fun test() = println("test function")

    val test = object {
        operator fun invoke() = println("test invocation")
    }

    test() // Prints: "test function"
}
Run Code Online (Sandbox Code Playgroud)

但是范围也有一些奇怪的互动.如果我在外面移动函数声明:

fun test() = println("test function")

fun main(args: Array<String>) {
    val test = object {
        operator fun invoke() = println("test invocation")
    }

    test() // Prints "test invocation"
}
Run Code Online (Sandbox Code Playgroud)

同样,如果我将对象移到外面,这也会编译:

val test = object {
    operator fun invoke() = println("test invocation")
}

fun main(args: Array<String>) {
    fun test() = println("test function")
    test() // Prints: "test function"
}
Run Code Online (Sandbox Code Playgroud)

我也可以将它们移到外面:

val test = object {
    operator fun invoke() = println("test invocation")
}

fun test() = println("test function")

fun main(args: Array<String>) {
    test() // Prints: "test function"
}
Run Code Online (Sandbox Code Playgroud)

但是,如果我test使用类名重载,它不会编译:

class test {} // Does not compile

fun test() = println("test function")

val test = object {
    operator fun invoke() = println("test invocation")
}
Run Code Online (Sandbox Code Playgroud)

尝试编译此程序会导致以下错误:

Error:(1, 6) Conflicting overloads: public fun test(): Unit defined in root package in file Simplest version.kt, public constructor test() defined in test, public val test: Any defined in root package in file Simplest version.kt, public final class test defined in root package in file Simplest version.kt
Error:(1, 6) Conflicting declarations: public fun test(): Unit, public constructor test(), public val test: Any, public final class test
Error:(2, 0) Conflicting overloads: public fun test(): Unit defined in root package in file Simplest version.kt, public constructor test() defined in test, public val test: Any defined in root package in file Simplest version.kt, public final class test defined in root package in file Simplest version.kt
Error:(3, 4) Conflicting declarations: public fun test(): Unit, public constructor test(), public val test: Any, public final class test
Run Code Online (Sandbox Code Playgroud)

但是在使用嵌套作用域时它会编译:

class test {
    constructor() {
        println("test constructor")
    }
}

fun main(args: Array<String>) {
    fun test() = println("test function")

    val test = object {
        operator fun invoke() = println("test invocation")
    }

    test() // Prints: "test function"
}
Run Code Online (Sandbox Code Playgroud)

伴随对象和构造函数之间也存在一些歧义:

class test {
    constructor() {
        println("test constructor")
    }

    companion object {
        operator fun invoke() = println("test companion invocation")
    }
}

fun main(args: Array<String>) {
    test() // Prints: "test constructor"
}
Run Code Online (Sandbox Code Playgroud)

不知何故,以下示例也编译:

class test {
    constructor() {
        println("test constructor")
    }

    companion object {
        operator fun invoke() = println("test companion invocation")
    }
}

fun main(args: Array<String>) {
    test() // Prints: "test constructor"

    val test = object {
        operator fun invoke() = println("test invocation")
    }

    test() // Prints: "test invocation"

    fun test() = println("test function")

    test() // Prints: "test function"
}
Run Code Online (Sandbox Code Playgroud)

这更不直观:

class test {
    constructor() {
        println("test constructor")
    }

    companion object {
        operator fun invoke() = println("test companion invocation")
    }

    operator fun invoke() = println("test invocation overload")
}

fun main(args: Array<String>) {
    val test = test() // Prints: "test constructor"

    val test1 = test() // Prints: "test invocation overload"
}
Run Code Online (Sandbox Code Playgroud)

重载命名成员的规则是什么?为什么Kotlin编译器会在同一范围内接受可调用变量和同名函数,但不存在同名类(在某些情况下,而不是其他类)?此外,如果在具有相同调用站点语法的范围构造函数或伴随对象的存在下调用如何工作?

leo*_*mer 4

从我在kotlin-spec.asc#order-of-evaluation中看到的来看,有三个规则在起作用(不幸的是,文本在某些方面不完整):

  1. 具有最佳类型匹配的表达式(不会出现在您的问题中)
  2. 本地声明优先于非本地声明。这也称为阴影。

    简单的名称是一个标识符。它的含义取决于具有该名称的符号在范围内。如果只有具有该名称的符号在范围内,则简单名称会引用它。如果范围内有多个具有该名称的符号,则非正式地,将选择其声明与简单名称的出现“最接近”的符号。有关更精确的规则,请参阅 TODO

  3. 如果所有同名符号处于同一级别,则函数优先于带有调用的属性

    实际的顺序是

    • 函数描述符(fun foo()在包含类中)
    • 调度接收者(请参阅declaring-extensions-as-members
      • 如果调度接收者和分机接收者的成员之间发生名称冲突,则分机接收者优先。

    • 扩展接收器 ( fun A.foo() defined outside of the class)
    • 任务优先级排序器(据我所知,按类型找到最佳匹配,或者例如当有一些默认参数时。我认为这是invoke属于该类别)

如果您将其应用于最后一个示例:

class test {
    constructor() {
        println("test constructor")
    }

    companion object {
        operator fun invoke() = println("test companion invocation")
    }

    operator fun invoke() = println("test invocation overload")
}

fun main(args: Array<String>) {
    val test = test() // Prints: "test constructor" //you create a local variable with invoke. Constructor is executed.

    val test1 = test() // Prints: "test invocation overload" //invoke of the local variable is called.

    test.Companion() //access the companions' invoke which is shadowed by the other invoke.
}
Run Code Online (Sandbox Code Playgroud)