之前我们问过:
提到了isSynthetic,注意今天的问题也是个类似的问题。
首先我们编写个接口:
interface Animal<T>{
void test(T t);
}
这个接口有个实现类:
class Dog implements Animal<String>{
@override
public void test(String str){
}
}
符合我们平时的写法对吧。
但是你仔细推敲一下:
接口 Animal 类的泛型,在编译成 class 后,会经历泛型擦除,会变成这样:
interface Animal{
void test(Object obj);
}
而实现类Dog里面有个方法test(String str)
,注意这个方法和接口类的方法参数并不一致。
那么也就是说,并没有实现接口中的方法。
但是,接口的方法,实现类是必须实现的。
问题来了:
- 为何不报错呢?
- 除了这个场景,编译期间还有哪里有类似的处理方式么?(可不回答)
更多问答 >>
-
2020-10-03 11:43
-
每日一问 | 启动了Activity 的 app 至少有几个线程?
2020-10-12 00:47 -
每日一问 | 玩转 Gradle,可不能不熟悉 Transform,那么,我要开始问了。
2020-10-26 23:45 -
每日一问 | 关于 RecyclerView$Adapter setHasStableIds(boolean)的一切
2020-10-26 23:44 -
每日一问 | 属性动画与硬件加速的相遇,不是你想的那么简单?
2020-10-26 23:45 -
2020-08-26 21:11
-
2020-08-23 23:54
-
每日一问 | apply plugin: 'com.android.application' 背后发生了什么?
2020-08-16 19:56 -
每日一问| View 绘制的一个细节,如何修改 View 绘制的顺序?
2020-08-12 10:21 -
每日一问 | 比 removeView 更轻量的操作,你了解过吗?
2020-07-27 01:14
编译题目给出代码,再像上次那样javap看一下Dog的字节码:
javap -v Dog.class:可以看到,Dog里面除了我们在代码中编写的
第一个(ACC_PUBLIC)不用说,从第三个(ACC_SYNTHETIC)可以知道这个方法是编译器帮我们生成的,而且我们在代码中不能通过常规方式来访问。中间的ACC_BRIDGE是什么呢?正则表达式全局搜一下,看看在哪里声明:test(String)
,还多了一个test(Object)
(还可以看到它在checkcast之后也会调用test(String)
方法),注意看这个多出来的test(Object)
方法的flags:ACC_PUBLIC 、 ACC_BRIDGE 、 ACC_SYNTHETIC 。emmmm,跟上次的SYNTHETIC一样,也是在Modifier里面。
进一步会发现,这个BRIDGE只在Executable(Method的父类)中的isBridgeMethodInternal
方法中有使用到:最终是在Method的
这个由编译器帮我们生成的isBridge
方法里调用。看文档注释:如果是bridge方法就return true。bridge是桥,结合刚刚的字节码就很容易想到:test(Object)
方法,就像一座桥一样,将接口方法与我们自己实现的真实方法连接起来。回到题目中的问题:
为什么没有手动实现接口经过泛型擦除后的test(Object)
方法也能正常通过编译呢?因为这一步编译器已经帮我们做了,在自动生成test(Object)
方法后,还会加上一个ACC_BRIDGE标记,在程序中可以通过Method的isBridge
方法来判断。除了这个场景,编译期间还有哪里有类似的处理方式?
emmmm,上次说的那个内部类私有变量的问题,也类似:内部类在编译后变成了普通类,它的私有变量原则上外部类不能直接引用,所以也会生成对应的setter
、getter
方法。除了私有变量,私有方法也一样,都会额外生成一个外部可访问的代理方法。11:04 补充:哈哈哈,其实我也觉得,上面没有说到点。刚刚准备关闭打开的源码文件的时候,目光刚好放在了Modifier声明BRIDGE的下方,看到一个VARARGS,猛然一想:对哦!varargs其实也是数组!于是马上改代码测试了一下,发现果然跟我想的一样!我把接口刚刚写的时候,发现是我把本来的
test
方法的ACC_BRIDGE标记误认为是varargs方法自动生成的,写到一半发现不对,去掉接口实现之后再看了下字节码,发现并没有额外生成一个参数为数组的方法。。。。好尴尬啊,图都截好了。跟着缘佬的步伐
实际是这样的
1. 子类泛型方法的反编译
2.子类编译器生成的方法
3 系统生成的字节码无局部变量表 但是有通过aload_0,aload_1从局部变量表把参数load到操作数栈? 相邻俩个方法的局部变量表是如何复用的呢
忘了是在那看到过的了。。。生成了一个桥接方法。
下一篇还和这个相关,猜猜是什么~