0x1、原理
@JvmOverloads的作用 → 告知编译器自动生成多个该方法的重载
就是不用自己写重载方法,kt会帮你自动生成,比如下面的代码:
@JvmOverloads fun search(name: String? = null, price: Float = 0.0f, kind: Int = -1) {}
等价于你在Java中声明三个重载方法:
void search(String name) void search(String name, float price) void search(String name, float price, int kind)
但!底层真的是转换成了这样三个方法吗?实际上 并不是!反编译下字节码:
生成了一个 search$default()
的方法,其他方法传参调用此方法。
令人不解的应该是&1、&2、&4,以及传参4,6,7 了,这些数字是干嘛的,还有怎么来的?
加上调用重载方法相关的代码:
fun main() { val book = Book() book.search("Kotlin") book.search("Kotlin", 66.6f) book.search("kotlin", kind = 1) book.search(price = 100.0f) }
反编译下字节码:
传入的第五个参数依次为:6、4、2、5,调用传参少,值反而大,跟下6时searchBook$default走的逻辑:
6 → 0110 1 → 0001 | 6 & 1 → 0000 => 结果为0,不重置var1的值 2 → 0010 | 6 & 2 → 0010 => 结果不为0,重置var2的值 4 → 0100 | 6 & 4 → 0100 => 结果不为0,重置var3的值
看到这里,思维敏捷的朋友应该看出端倪了,不急,再试试4时:
4 → 0100 1 → 0001 | 4 & 4 → 0000 => 结果为0,不重置var1的值 2 → 0010 | 4 & 2 → 0000 => 结果为0,不重置var2的值 4 → 0100 | 4 & 4 → 0100 => 结果不为0,重置var3的值
还没get√到的同学想想这两个式子:6 = 0 + 2 + 4,4 = 0 + 0 + 4
懂了吧,就是每个参数对应一个值 → 2^(第几个参数-1)
,有传这个参数就不加,没传就加上。
不信?再加个参数试试:
@JvmOverloads fun search(name: String? = null, price: Float = 0.0f, kind: Int = -1, author: String? = null) { }
看下反编译效果:
不难看出:
14 = 0 + 2 + 4 + 8 12 = 0 + 0 + 4 + 8 10 = 0 + 2 + 0 + 8
根据这个值来判断某位置的参数是否传参,没有直接赋初值,减少了非必要重载方法的生成,妙啊!!!
0x2、一个小细节
在实际开发中,@JvmOverloads最常用的场景莫过于 自定义View 时偷懒不用写这三个构造方法:
当你继承一个View,AS会贴心的提醒你,要不要加上这个@JvmOverloads:
点击之后自动加上,很香~
看着还行,当我们在布局文件中引用这个自定义的EditText,会发现,根本拿不到焦点,没办法输入???
其实跟下源码就知道了:
这个默认的值可不是0,应该是R.attr.editTextStyle,所以解决方法也很简单,把0改成这个值就好了:
其他控件也可能存在这种情况,当自定义View实现样式问题时,用到了@JvmOverloads,可以试着排查看看~