当前位置: 首页 > news >正文

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

导读大纲

      • 1.1.1 从 Java 调用扩展函数
      • 1.1.2 扩展函数无法重载

1.1.1 从 Java 调用扩展函数

  1. 在编译器底层下,扩展函数是一种静态方法,它接受接收器对象作为第一个参数
    • 调用它不涉及创建适配器对象任何其他运行时开销
    • 这使得从 Java 使用扩展函数变得非常简单
      1. 调用静态方法传递接收器对象实例即可
    • 与其他顶级函数一样
      1. 包含该方法的 Java 类的名称声明该函数的文件名决定
      2. 关于顶级函数和顶级属性–传送门
    • <1> 比方说,lastChar扩展函数是在 StringUtil.kt 文件中声明的
      1. 那么在Java中调用时就需要使用"StringUtilKt"类名
      2. 关于如何使用JvmName注解修改类名–传送门
/* Java */
char c = StringUtilKt.lastChar("Java");    // <1>
  1. 该扩展函数被声明为顶级函数,因此编译为静态方法
    • <1> 可以从 Java 中静态导入 lastChar 方法
      1. 从而简化使用, 只需 lastChar(“Java”) 即可
      2. 注意使用的导入方式"import static xxx"
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 修饰符在子类中提供一个实现
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 类的重载实现
fun main() {val view: View = Button()            // <1>view.click()                         // <2>// Button clicked                     
}
  1. 但对于扩展函数来说,情况并非如此
    • 扩展函数不是类的一部分,它们是在类的外部声明
    • 比如, 在 View 和 Button 类之外
      1. 定义 View.showOff() 和 Button.showOff() 扩展函数
        title
    • 即使可以为基类及其子类定义具有相同名称和参数类型的扩展函数
    • <1> 但调用的函数取决于变量声明的静态类型, 该类型在编译时确定
      1. 在对 view 变量调用 showOff 时,即使值的实际类型是 Button
        • 也会调用View类型的showOff方法,因为view变量的编译时类型是"View"
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 也会以同样的方式调用指定类型的扩展函数
/* Java */
class Demo {public static void main(String[] args) {View view = new Button();ExtensionsKt.showOff(view);// I'm a view!}
}
  1. 综上所述,如果类中的成员函数与扩展函数的签名相同,则成员函数总是优先
    • 集成开发环境也会警告您, 扩展函数已被成员函数覆盖

http://www.mrgr.cn/news/34347.html

相关文章:

  • 重学 Android 自定义 View 系列(六):环形进度条
  • 内网对抗-信息收集篇SPN扫描DC定位角色区域定性服务探针安全防护凭据获取
  • 【MinIO】Python 运用 MinIO 实现简易文件系统
  • 解决msvcr100.dll丢失的方法,5个实测可靠的解决方法
  • Elman 神经网络算法详解
  • 河南省测绘资质管理制度解析
  • 云原生周刊:Artifact Hub 成为 CNCF 孵化项目|2024.9.23
  • 项目实现:云备份服务端③(热点模块、服务端业务处理模块实现)
  • 三线城市的女玩家们不想“谈恋爱”,小游戏掘金新蓝海
  • 【Transformers基础入门篇4】基础组件之Model
  • 干货:企业微信批量删除客户指南!
  • 13.第二阶段x86游戏实战2-动态模块地址
  • 【Go】Go语言中深拷贝和浅拷贝
  • Java详细学习路线:从入门到精通的全方位指南
  • 数字人起飞!字节Loopy对口型功能上线 可根据语境匹配表情和情绪
  • 一个可以在线制作样本册,拥有海量样本图册模板可以套用的网站
  • Vert.x,Core - Future
  • 视频无损压缩工具+预览视频生成工具
  • Java 中使用 Gson 实现深度克隆 #什么是深克隆与浅克隆?#clone方法为什么不能直接通过某个对象实例在外部类调用?
  • 我设置了路由器自动切换ip,这会让我的账号登录地址经常改变吗
  • 奔驰「进退」两难
  • Webpack 常见配置项
  • apply、call和bind的作用和区别
  • 装饰器模式
  • Vue使用Vue Router路由:开发单页应用
  • 【网络协议栈】传输层的意义 和 UDP协议结构的解析(内含逻辑图解通俗易懂)