java8 如何判断两个方法引用是一个方法?

闪小达 发布于 2016/01/31 14:32
阅读 924
收藏 1

Wo wo = new Wo();
Consumer<Integer> cc = wo::nihao;
Consumer<Integer> cc1 = wo::nihao;
System.out.println( cc == cc1 );
System.out.println( cc.equals( cc1 ) );
false

false

有没有办法判断呢?

加载中
0
大白菜丶嘿咻
大白菜丶嘿咻
如果有两个同类型的对象,分别叫作a和b,那么您也许不知道如何为这两个对象同时调用一个f()方法:
class Banana { void f(int i) { /* ... */ } }
Banana a = new Banana(), b = new Banana();
a.f(1);
b.f(2);

若只有一个名叫f()的方法,它怎样才能知道自己是为a还是为b调用的呢?
为了能用简便的、面向对象的语法来书写代码——亦即“将消息发给对象”,编译器为我们完成了一些幕后工作。其中的秘密就是第一个自变量传递给方法f(),而且那个自变量是准备操作的那个对象的句柄。所以前述的两个方法调用就变成了下面这样的形式:
Banana.f(a,1);
Banana.f(b,2);

这是内部的表达形式,我们并不能这样书写表达式,并试图让编译器接受它。但是,通过它可理解幕后到底发生了什么事情。

假定我们在一个方法的内部,并希望获得当前对象的句柄。由于那个句柄是由编译器“秘密”传递的,所以没有标识符可用。然而,针对这一目的有个专用的关键字:this。this关键字(注意只能在方法内部使用)可为已调用了其方法的那个对象生成相应的句柄。可象对待其他任何对象句柄一样对待这个句柄。但要注意,假若准备从自己某个类的另一个方法内部调用一个类方法,就不必使用this。 Think in java 第4.2.5章。 感觉能解释你的问题

闪小达
闪小达
我可能要理解一会儿。。。谢谢
0
Cat7373
Cat7373
我记得 :: 不是 Java 的语法...
闪小达
闪小达
是java8的方法引用符
0
景愿
景愿

这个确实是个难题,java8中通过lambda实现的接口函数在编译时使用内部匿名类重写的,就像你的cc和cc1是两个完全不同class的实例,所以如果要比较两个Lambda的源方法是否为同一个,我想除了比较字节码外暂时还没有有效的办法,期待高手解答。

但是可以通过设计来避免此类问题,比如cc和cc1我想在线程安全的情况下完全可以复用,为何要创建出两个Consumer呢?

景愿
景愿
回复 @闪小达 : 不要过度依赖java8的lambda,他不是万能的,该对象的时候还是用对象和接口来提供API
闪小达
闪小达
我在做一个监听事件 和 移除事件的模块 现在可以增加监听 但要移除方法引用就不行了
闪小达
闪小达
让使用这个api的人维护 方法引用 太不人道了
0
陈大炮
陈大炮
重写equal
0
魔仙剑痴
重写equal和hashcode
0
妹子楼顶有鸽子
妹子楼顶有鸽子
被调用方法中附带返回当前调用方的栈帧数据,比较下
0
羊驼君
羊驼君
0111010001101111011011110010000001111001011011110111010101101110011001110010000001110100011011110110111100100000011100110110100101101101011100000110110001100101
0
木有芒果
木有芒果

引用来自“景愿”的评论

这个确实是个难题,java8中通过lambda实现的接口函数在编译时使用内部匿名类重写的,就像你的cc和cc1是两个完全不同class的实例,所以如果要比较两个Lambda的源方法是否为同一个,我想除了比较字节码外暂时还没有有效的办法,期待高手解答。

但是可以通过设计来避免此类问题,比如cc和cc1我想在线程安全的情况下完全可以复用,为何要创建出两个Consumer呢?

确实这个楼主的提问如你所说,是不同实例所以不可能是相等的,但是这里稍微纠正一下这里的一个说法,就是这句

编译时使用内部匿名类重写的

这里lambda表达式被编译的时候其实不是使用的匿名内部类重写的,只能说是最终运行的时候才是按照匿名内部类来实现的,为啥呢?要是编译就按照匿名内部类的话,那lambda表达式只能算是匿名内部类的语法糖了,其实不然,它和内部类还是有些本质的区别,以及性能上的巨大差别

举个例子:

函数式接口Function,我先用内部类实现,可以看到,确实编译后会生成两个class,test$1就是那个Function的匿名内部类


但是同样的Function接口,我用lambda表达式实现之后,只会有一个class文件而已


所以从这里就可以看出来,编译之后,lambda表达式并不是按照匿名内部类来编译的

其实lambda表达式在被编译器编译时,用到了invokedynamic字节码指令,这是java7的新特性

编译时,lambda表达式会被翻译成一个invokedynamic调用点,然后当调用时这个时候才会返回Lambda表达式转化成的函数式接口实例,对了,那个调用点也叫lambda工厂吧,我记得好像叫这个名字

再检查哈这个字节码文件,可以一眼就看到invokedynamic指令了



返回顶部
顶部