【解密 Kotlin 扩展函数】扩展函数的底层原理(十八)

导读大纲

      • [1.1.1 从 Java 调用扩展函数](#1.1.1 从 Java 调用扩展函数)
      • [1.1.2 扩展函数无法重载](#1.1.2 扩展函数无法重载)

1.1.1 从 Java 调用扩展函数

  1. 在编译器底层下,扩展函数是一种静态方法 ,它接受接收器对象作为第一个参数
    • 调用它不涉及创建适配器对象任何其他运行时开销
    • 这使得从 Java 使用扩展函数变得非常简单
      1. 调用静态方法传递接收器对象实例即可
    • 与其他顶级函数 一样
      1. 包含该方法的 Java 类的名称声明该函数的文件名决定
      2. 关于顶级函数和顶级属性--传送门
    • <1> 比方说,lastChar扩展函数是在 StringUtil.kt 文件 中声明的
      1. 那么在Java中调用时就需要使用"StringUtilKt"类名
      2. 关于如何使用JvmName注解修改类名--传送门
kotlin 复制代码
/* Java */
char c = StringUtilKt.lastChar("Java");    // <1>
  1. 该扩展函数被声明为顶级函数 ,因此编译为静态方法
    • <1> 可以从 Java 中静态导入 lastChar 方法
      1. 从而简化使用 , 只需 lastChar("Java") 即可
      2. 注意使用的导入方式"import static xxx"
kotlin 复制代码
import static strings.StringFunctions.lastChar;   // <1>
public class Example {
    public static void main(String[] args){
        System.out.println(lastChar("Java"));       // <1>
    }
}
  1. 扩展函数的静态特性 还意味着子类中不能重载扩展函数

1.1.2 扩展函数无法重载

  1. 在 Kotlin 中,方法重载与成员函数一样有效 ,但不能重载扩展函数
    • <1> 假设有两个类: View 和 Button
      1. Button 是 View 的子类,它重载父类中的 click 函数
    • <2> 要实现这一点,可以使用 open 修饰符标记View和click , 以允许重载
      1. 使用 override 修饰符在子类中提供一个实现
kotlin 复制代码
open class View {
    open fun click() = println("View clicked")       // <2>
}
class Button: View() {              // <1>
    override fun click() = println("Button clicked") // <2>
}
  1. 如果你声明一个 View 类型的变量
    • <1> 那么你就可以在该变量中存储 Button 类型的值
      1. 因为 Button 是 View 的子类型
    • <2> 如果在该变量上调用常规方法(如 click)
      1. 该方法在 Button 类中被重载 ,则将使用 Button 类的重载实现
kotlin 复制代码
fun main() {
    val view: View = Button()            // <1>
    view.click()                         // <2>
    // Button clicked                     
}
  1. 但对于扩展函数来说,情况并非如此
    • 扩展函数不是类的一部分 ,它们是在类的外部声明
    • 比如, 在 View 和 Button 类之外
      1. 定义 View.showOff() 和 Button.showOff() 扩展函数
    • 即使可以为基类及其子类 定义具有相同名称和参数类型的扩展函数
    • <1> 但调用的函数取决于变量声明的静态类型 , 该类型在编译时确定
      1. 在对 view 变量调用 showOff 时,即使值的实际类型是 Button
        • 也会调用View类型的showOff方法 ,因为view变量的编译时类型是"View"
kotlin 复制代码
fun View.showOff() = println("I'm a view!")
fun Button.showOff() = println("I'm a button!")
fun main() {
    val view: View = Button()
    view.showOff()                      // <1>
    // I'm a view!
}
  1. 回忆一下上面所说的扩展函数在 Java 中被编译为静态函数
    • 接收器对象是第一个参数 , Java 也会以同样的方式调用指定类型的扩展函数
kotlin 复制代码
/* Java */
class Demo {
    public static void main(String[] args) {
        View view = new Button();
        ExtensionsKt.showOff(view);
        // I'm a view!
    }
}
  1. 综上所述,如果类中的成员函数与扩展函数的签名相同 ,则成员函数总是优先
    • 集成开发环境也会警告 您, 扩展函数已被成员函数覆盖
相关推荐
CLX0505几秒前
SQL排查JOIN查询中索引失效的常见情况_数据类型隐式转换
jvm·数据库·python
思麟呀4 分钟前
在C++基础上理解Csharp-2
开发语言·jvm·c++·c#
桀人5 分钟前
类和对象——上篇
开发语言·c++
ZC跨境爬虫7 分钟前
跟着 MDN 学 HTML day_57:(HTML 表格进阶特性与无障碍实践)
java·前端·javascript·ui·html·音视频
zzzsde9 分钟前
【Linux】线程概念与控制(3):线程ID&&C++封装线程
linux·运维·服务器·开发语言·算法
消失的旧时光-194315 分钟前
C 语言如何实现“面向对象”?—— 从 struct + 函数指针,到 Linux 内核设计思想
linux·c语言·开发语言
鱼鳞_19 分钟前
苍穹外卖-Day01(开发环境搭建)
java·spring boot·spring·maven
小短腿的代码世界33 分钟前
Qt时间日期处理与QTimer高级应用:从毫秒级精度到跨平台定时器的完整架构解析
开发语言·qt·架构
TAN-90°-38 分钟前
Java 6——成员变量初始值 object equals和== toString instanceof 参数传递问题
java·开发语言
中新传媒40 分钟前
德宸堂心理双师同诊
java·前端·数据库