Kotlin拓展函数的真身
kotlin也写了很长一段时间了,香是真的很香这个东西。但是很多东西也是不求甚解,都是直接开始用,但是为什么我也不关心。举个栗子,就拿拓展函数来说。
ImageExtension.kt
fun Double.dp(): Int {
return ImageUtils.dip2px(this.toFloat())
}
fun Float.dp(): Int {
return ImageUtils.dip2px(this.toFloat())
}
fun Int.dp(): Int {
return ImageUtils.dip2px(this.toFloat())
}
复制代码
val width = 18F.dp()
这个真的香吧,但是真实的这个语法糖到底是什么呢。
返本归元
先介绍个工具,Android Studio的工具栏有个tools/Kotlin/Show Kotlin ByteCode。然后在你的kotlin类上就可以直接看到kotlin类生成的字节码。
先抛问题在来分析。
java能不能调用到kotlin到拓展函数?
当然可以了。下面的代码块内就是18F.dp()的java写法。
int width=ImageExtensionKt.dp(18f);
惊不惊喜,意不意外。也就是说我们写的拓展函数其实也就是个静态方法,只是把我们拓展的类当作一个静态参数传递给了静态方法内。
深入探索
上面介绍的工具还是要用下,看看到底字节码上的拓展函数是怎么样的。
public final class com/xxxx/xxxx/ImageExtensionKt {
// access flags 0x19
public final static dp(D)I
// annotable parameter count: 1 (visible)
// annotable parameter count: 1 (invisible)
L0
LINENUMBER 47 L0
DLOAD 0
D2F
INVOKESTATIC com/xxx/xxx/ImageUtils.dip2px (F)I
IRETURN
L1
LOCALVARIABLE $this$dp D L0 L1 0
MAXSTACK = 2
MAXLOCALS = 2
// access flags 0x19
public final static dp(F)I
// annotable parameter count: 1 (visible)
// annotable parameter count: 1 (invisible)
L0
LINENUMBER 51 L0
FLOAD 0
INVOKESTATIC com/xxx/xxx/ImageUtils.dip2px (F)I
IRETURN
L1
LOCALVARIABLE $this$dp F L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
// access flags 0x19
public final static dp(I)I
// annotable parameter count: 1 (visible)
// annotable parameter count: 1 (invisible)
L0
LINENUMBER 55 L0
ILOAD 0
I2F
INVOKESTATIC com/xxx/xxx/ImageUtils.dip2px (F)I
IRETURN
L1
LOCALVARIABLE $this$dp I L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
}
简单分析下这个拓展函数类。
先给各位把字节码的基础简单的介绍下,表格会简单的把字节码里的类型和Java的类型做一次映射。
返回值
Type |
类型 |
---|---|
D |
double |
V |
void |
Z |
boolean |
C |
char |
B |
byte |
S |
short |
I |
int |
F |
float |
J |
long |
L |
Object |
操作符
操作符 |
类型 |
---|---|
INVOKESTATIC |
调用静态函数 |
INVOKESPECIAL |
super |
INVOKEVIRTUAL |
调用当前类的某个方法 |
接下来开始逐行分析,public final static dp(D)I,dp代表方法名,而D则代表参数传入类型为Double,而I则代表返回类型是一个Int。用java来分析这个代码就是生成了一个dp的静态函数,传入参数是一个Double,返回参数是一个Int。
跳过行号分析,直接进入DLOAD 0, LOAD的含义是将一个局部变量加载到操作栈,D上面的表中代表了double,所以解释起来获取第一个传入参数。
D2F各位老哥可以用英文大声朗读下,double to float。然后把这个压入操作栈中。
INVOKESTATIC com/xxx/xxx/ImageUtils.dip2px (F)I 先参考下上面的操作符,INVOKESTATIC调用静态方法。com/xxx/xxx/ImageUtils.dip2px 类名方法名, (F)I入参返回值。同时将结果压入操作栈中。
IRETURN 返回上面栈的操作结果。
简单分析完字节码的操作之后,得出来的结论基本就是生成了一个dp的静态函数,以当前的拓展类作为第一个入参。
总结
这个文章其实吧是最近一个前同事去面试的时候被问到的,我其实也有点蒙蔽。但是kotlin和java最后编译出来的产物其实都是字节码,那么我们只要从它们最后编译出来的产物去逆向分析下,其实就能得到它们真实的原因了。
最后还是要说语法糖还是真香的。
- HBase Region自动切分细节
- eclipse搭建ssh后台
- 解决mysql漏洞 Oracle MySQL Server远程安全漏洞(CVE-2015-0411)
- im4java包处理图片
- centOS7 mini配置linux服务器(五) 安装和配置tomcat和mysql
- RedisPool操作Redis,工具类实例
- centOS7 mini配置linux服务器(四) 配置jdk
- 老司机教你“飙”EventBus3
- Android listView异步下载和convertView复用产生的错位问题
- 实用Android 屏幕适配方案分享
- java-FFmpeg(一) 实现视频的转码和截图功能
- websocket(二) websocket的简单实现,识别用户属性的群聊
- websocket教程(一) 非常有趣的理解websocket
- 前端插件——头像截图上传插件的使用(带后台)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法