【解密 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. 综上所述,如果类中的成员函数与扩展函数的签名相同 ,则成员函数总是优先
    • 集成开发环境也会警告 您, 扩展函数已被成员函数覆盖
相关推荐
Scott9999HH3 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔3 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社4 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海4 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖5 小时前
JDK 26 新特性详解
java·开发语言
马优晨6 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区8 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大8 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai8 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说9 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json