ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
**Kotlin是一种静态类型语言,我们创建的每个对象不仅具有运行时,还具有编译时类型,开发人员必须明确指定(在Kotlin中可以推断)**。在**使用扩展函数时,要清楚地了解静态和动态调度之间的区别**。 ### 1.静态与动态调度 由于这个内容可能大部分读者都没有接触过,所以我们举一个Java例子,帮助大家理解。已知我们有以下类: ``` class Base { public void fun() { System.out.println(("I'm Base foo! ")); } } class Extended extends Base { @Override public void fun() { System.out.println(("I'm Extended foo! ")); } } Base base = new Extended(); base.fun(); ``` 对于以上代码,我们**声明一个名为base的变量,它具有编译时类型Base和运行时类型Extended**。当我们调用时,base.foo()将动态调度该方法,这意味着运行时类型(Extended)的方法被调用。 当我们调用重载方法时,调度变为静态并且仅取决于编译时类型。 ``` void foo(Base base) { ... } void foo(Extended extended) { ... } public static void main(String[] args) { Base base = new Extended(); foo(base); } ``` 在这种情况下,**即使base本质上是Extended的实例,最终还是会执行Base的方法**。 ### **2. 扩展函数始终静态调度** 可能你会好奇,这和扩展有什么关系?我们知道,扩展函数都有一个接收器(receiver),由于接收器实际上只是字节代码中编译方法的参数,因此你可以重载它,但不能覆盖它。这可能是**成员和扩展函数之间最重要的区别:前者是动态调度的,后者总是静态调度的**。 为了便于理解,我们举一个例子: ``` open class Base class Extended: Base() fun Base.foo() = "I'm Base.foo! " fun Extended.foo() = "I'm Extended.foo! " fun main(args: Array<String>) { val instance: Base = Extended() val instance2 = Extended() println(instance.foo()) println(instance2.foo()) } ``` 正如我们所说,**由于只考虑了编译时类型,第1个打印将调用`Base.foo()`,而第2个打印将调用`Extended.foo()`**。 ``` I'm Base.foo! I'm Extended.foo! ``` ### 3.类中的扩展函数 **如果我们在类的内部声明扩展函数,那么它将不是静态的。如果该扩展函数加上open关键字,我们可以在子类中进行重写(override)**。这是否意味着它将被动态调度?这是一个比较尴尬的问题:**当在类内部声明扩展函数时,它同时具有调度接收器和扩展接收器**。 ***** **调度接收器和扩展接收器**的概念 * **扩展接收器(extension receiver)**:与Kotlin扩展密切相关的接收器,表示**我们为其定义扩展的对象**。 * **调度接收器(dispatch receiver)**:扩展被声明为成员时存在的一种特殊接收器,它**表示声明扩展名的类的实例**。 ***** ``` class X { fun Y.foo() = " I'm Y.foo" } ``` 在上面的例子中,**X是调度接收器而Y是扩展接收器。如果将扩展函数声明为open,则它的调度接收器只能是动态的,而扩展接收器总是在编译时解析**。 这样说你可能还不是很明白,我们还是举一个例子帮助理解: ``` open class Base class Extended : Base() open class X { open fun Base.foo() { println("I'm Base.foo in X") } open fun Extended.foo() { println("I'm Extended.foo in X") } fun deal(base: Base) { base.foo() } } class Y : X() { override fun Base.foo() { println("I'm Base.foo in Y") } override fun Extended.foo() { println("I'm Extended.foo in Y") } } X().deal(Base()) // 输出I'm Base.foo in X Y().deal(Base()) // 输出I'm Base.foo in Y — 即dispatch receiver被动态处理 X().deal(Extended()) // 输出I'm Base.foo in X — 即extension receiver被静态处理 Y().deal(Extended()) // 输出I'm Base.foo in Y ``` 聪明的你可能会注意到,**Extended扩展函数始终没有被调用,并且此行为与我们之前在静态调度例子中所看到的一致。决定两个Base类扩展函数执行哪一个,直接因素是执行deal方法的类的运行时类型**。 通过以上例子,我们可以总结出扩展函数几个需要注意的地方: * **如果该扩展函数是顶级函数或成员函数,则不能被覆盖**; * **我们无法访问其接收器的非公共属性**; * **扩展接收器总是被静态调度**。