合成和桥接方法

如果您曾经玩过反射并执行了getDeclaredMethods()您可能会感到惊讶。 您可能会获得源代码中不存在的方法。 或者,也许您看了一些方法的修饰符,发现其中一些特殊方法是易变的。 顺便说一句:对于Java采访来说,这是一个令人讨厌的问题,“当方法易变时,这意味着什么?” 正确的答案是,方法不能是易变的。 同时,在getDeclaredMethods()甚至getMethods()返回的方法中,可能存在某些方法,其中Modifier.isVolatile(method.getModifiers())为true。

这是项目转换器的用户之一发生的 。 他意识到,交换器(本身会深入挖掘Java的黑暗细节)生成的Java源代码无法使用关键字volatile作为方法的修饰符进行编译。 结果,它也不起作用。

那里发生了什么事? 桥接和合成方法是什么?

能见度

创建嵌套或嵌入式类时,可以从顶级类访问嵌套类的私有变量和方法。 这由不可变的嵌入式构建器模式使用 。 这是语言规范中定义的Java的明确定义的行为。

JLS7,6.6.1确定可访问性
…如果成员或构造函数被声明为私有,则访问为
当且仅当它出现在顶级类的主体中时才允许(第7.6节)
包含成员或构造函数的声明…

package synthetic;public class SyntheticMethodTest1 {private A aObj = new A();public class A {private int i;}private class B {private int i = aObj.i;}public static void main(String[] args) {SyntheticMethodTest1 me = new SyntheticMethodTest1();me.aObj.i = 1;B bObj = me.new B();System.out.println(bObj.i);}
}

JVM如何处理它? JVM不知道内部或嵌套类。 对于JVM,所有类都是顶级外部类。 所有类都被编译为顶级类,这就是那些不错的方法...$. .class ...$. .class文件已创建。

$ ls -Fart
../                         SyntheticMethodTest2$A.class  MyClass.java  SyntheticMethodTest4.java  SyntheticMethodTest2.java
SyntheticMethodTest2.class  SyntheticMethodTest3.java     ./            MyClassSon.java            SyntheticMethodTest1.java

如果创建嵌套类或内部类,它将被编译为完整的顶级类。

外层如何提供私有字段? 如果这些人进入了真正的顶级阶级并且是私人的,那么他们将如何从外部阶级中获得呢?

javac解决此问题的方式是,对于任何私有字段但从顶级类使用的字段,方法或构造函数,它都会生成综合方法。 这些合成方法用于到达原始私有字段/方法/构造函数。 这些方法的生成以巧妙的方式完成:仅生成真正需要并从外部使用的那些方法。

package synthetic;import java.lang.reflect.Constructor;
import java.lang.reflect.Method;public class SyntheticMethodTest2 {public static class A {private A(){}private int x;private void x(){};}public static void main(String[] args) {A a = new A();a.x = 2;a.x();System.out.println(a.x);for (Method m : A.class.getDeclaredMethods()) {System.out.println(String.format("%08X", m.getModifiers()) + " " + m.getName());}System.out.println("--------------------------");for (Method m : A.class.getMethods()) {System.out.println(String.format("%08X", m.getModifiers()) + " " + m.getReturnType().getSimpleName() + " " + m.getName());}System.out.println("--------------------------");for( Constructor<?> c : A.class.getDeclaredConstructors() ){System.out.println(String.format("%08X", c.getModifiers()) + " " + c.getName());}}
}

由于生成的方法的名称取决于实现方式,因此不能保证对上述程序的输出所能说的最多的是,在我执行该程序的特定平台上,它产生了以下输出:

2
00001008 access$1
00001008 access$2
00001008 access$3
00000002 x
--------------------------
00000111 void wait
00000011 void wait
00000011 void wait
00000001 boolean equals
00000001 String toString
00000101 int hashCode
00000111 Class getClass
00000111 void notify
00000111 void notifyAll
--------------------------
00000002 synthetic.SyntheticMethodTest2$A
00001000 synthetic.SyntheticMethodTest2$A

在上面的程序中,我们为字段x赋值,并且还调用了相同名称的方法。 需要这些来触发编译器生成综合方法。 您可以看到它生成了三种方法,大概是字段x的setter和getter以及方法x()的综合方法。 但是,这些综合方法未在getMethods()返回的下一个列表中列出,因为它们是综合方法,因此不适用于通用调用。 从这种意义上讲,它们是私有方法。

十六进制数字可以用作解释器,查看类java.lang.reflect.Modifier定义的常量:

00001008 SYNTHETIC|STATIC
00000002 PRIVATE
00000111 NATIVE|FINAL|PUBLIC
00000011 FINAL|PUBLIC
00000001 PUBLIC
00001000 SYNTHETIC

列表中有两个构造函数。 有一个私人的和一个合成的。 私有存在,因为我们定义了它。 另一方面,合成的存在是因为我们从外部调用了私有的。 到目前为止,桥接方法还没有。

泛型和继承

到目前为止还不错,但是我们仍然没有看到任何“易变”的方法。

查看java.lang.reflec.Modifier的源代码,您会看到常量0x00000040定义了两次。 一次是VOLATILE ,一次是BRIDGE (后者是私有程序包,不用于一般用途)。

要拥有这样一种方法,一个非常简单的程序就可以做到:

package synthetic;import java.lang.reflect.Method;
import java.util.LinkedList;public class SyntheticMethodTest3 {public static class MyLink extends LinkedList<String> {@Overridepublic String get(int i) {return "";}}public static void main(String[] args) {for (Method m : MyLink.class.getDeclaredMethods()) {System.out.println(String.format("%08X", m.getModifiers()) + " " + m.getReturnType().getSimpleName() + " " + m.getName());}}
}

我们有一个链表,该链表的方法get(int)返回String 。 我们不要讨论干净的代码问题。 这是演示该主题的示例代码。 干净的代码中也会出现相同的问题,尽管更复杂,并且在导致问题时更难指出问题所在。

输出显示:

00000001 String get
00001041 Object get

我们有两个get()方法。 一个出现在源代码中,另一个出现在合成和桥接中。 反编译器javap表示生成的代码是:

public java.lang.String get(int);Code:Stack=1, Locals=2, Args_size=20:   ldc     #2; //String2:   areturnLineNumberTable:line 12: 0public java.lang.Object get(int);Code:Stack=2, Locals=2, Args_size=20:   aload_01:   iload_12:   invokevirtual   #3; //Method get:(I)Ljava/lang/String;5:   areturn

有趣的是,这两种方法的签名是相同的,只是返回类型不同。 即使在Java语言中这是不可能的,但在JVM中允许这样做。 bridge方法不执行其他任何操作,而是调用原始方法。

为什么需要这种合成方法? 谁来使用它。 例如,想要使用类型MyLink的变量来调用方法get(int)MyLink

List<?> a = new MyLink();Object z = a.get(0);

它不能调用返回String的方法,因为List没有这样的方法。 为了使其更具说明性,让我们重写方法add()而不是get()

package synthetic;import java.util.LinkedList;
import java.util.List;public class SyntheticMethodTest4 {public static class MyLink extends LinkedList<String> {@Overridepublic boolean add(String s) {return true;}}public static void main(String[] args) {List a = new MyLink();a.add("");a.add(13);}
}

我们可以看到桥接方法

public boolean add(java.lang.Object);Code:Stack=2, Locals=2, Args_size=20:   aload_01:   aload_12:   checkcast       #2; //class java/lang/String5:   invokevirtual   #3; //Method add:(Ljava/lang/String;)Z8:   ireturn

不仅叫原版。 它还检查类型转换是否正确。 这是在运行时完成的,而不是由JVM本身完成的。 如您所料,它确实出现在第18行中:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Stringat synthetic.SyntheticMethodTest4$MyLink.add(SyntheticMethodTest4.java:1)at synthetic.SyntheticMethodTest4.main(SyntheticMethodTest4.java:18)

下次在面试中遇到关于不稳定方法的问题时,您可能比面试官了解的更多。

参考: Java Deep博客中JCG合作伙伴 Peter Verhas的合成方法和桥接方法 。

翻译自: https://www.javacodegeeks.com/2014/03/synthetic-and-bridge-methods.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/365159.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小米手机升级Android6,小米3能升级miui 6?小米3升级miui v6教程

小米3升级miui v6教程来咯~小编还带来了miui v6下载&#xff0c;MIUI V6系统有了很多的变化&#xff0c;小米3是可以升级miui6系统的哦~如果你不知道步骤的话&#xff0c;就请参考下文吧。第一步、下载工具包和线刷完整包&#xff0c;下载完成后解压(请检查线刷包文件后缀名是否…

hibernate使用sessionfactory插入数据

1.引入hibernate-core.jar 2.java代码 public void meth{ Session session sessionFactory.openSession();Transaction tr session.beginTransaction();//1.创建Customer对象//2.调用Session的save()方法&#xff0c;将Customer对象持久化session.save(jbpmTaskInherit);tr.c…

lisp 揭 院长_HISLISPACSRIS EMR系统简介

HIS(LIS、PACS、RIS、EMR)系统简介一、定义说明医院信息系统(Hospital Information System, HIS)&#xff0c;利用电子计算机和通讯设备&#xff0c;为医院所属各部门提供病人诊疗信息和行政管理信息的收集、存储、处理、提取和数据交换的能力&#xff0c;并满足所有授权用户的…

第一次java作业

转载于:https://www.cnblogs.com/TuTu-winer/p/10591021.html

推荐25款很棒的 HTML5 前端框架和开发工具【下篇】

快速&#xff0c;安全&#xff0c;响应式&#xff0c;互动和美丽&#xff0c;这些优点吸引更多的 Web 开发人员使用 HTML5。HTML5 有许多新的特性功能&#xff0c;允许开发人员和设计师创建应用程序和网站&#xff0c;带给用户桌面应用程序的速度&#xff0c;性能和体验。 这里…

创建Sonarqube项目

Sonarqube &#xff08;nee Sonar&#xff09;是炸弹。 不必每天检查一次&#xff0c;但是如果您对质量很认真&#xff0c;则可以在Sprint计划期间&#xff08;如果不是每周一次&#xff09;对其进行检查。 在nemo.sonarqube.com上查看示例项目&#xff0c;例如OpenJPA &#…

android18lazuli,The Rise And Fall Of Android 17

Hey guys, Tommy here. Today I’m bringing you another blog talking about Android 17. I’ve had mixed emotions on Android 17’s return in Dragon Ball Super since it originally happened. I feel like now with this whole Android 17‘a return is something I sho…

luogu P1462 通往奥格瑞玛的道路

嘟嘟嘟 这道题的题面相当的迷&#xff0c;我看了半天都没懂。最后看了题解的解释才懂。 他是这么个意思&#xff1a;对于所有能活着走到终点的路径&#xff0c;输出每一条路径中过路费最多的城市的最小值。 那么自然想到二分过路费&#xff0c;然后用dijkstra或spfa判断是否存在…

树莓派 红灯不亮_请问我的树莓派烧了系统后板子只有红灯亮,而act绿灯不亮,并且网口不插网线两个灯都是微微亮,请问?...

错误已找出&#xff0c;是镜像文件的问题。如果你的情况跟我下面的情况一样&#xff0c;可以把镜像重新下载一遍。以下是原回答&#xff1a;我的也是&#xff0c;买的3b。第一次烧录系统是成功的&#xff0c;TF卡插入之后&#xff0c;上电&#xff0c;power红灯亮&#xff0c;A…

Maven私服(Nexus)搭建总结

因为工作需要&#xff0c;第一次搭建Maven私服&#xff0c;使用的是目前最流行的Sonatype Nexus。搭建私服并不难&#xff0c;官网有很详细的说明&#xff0c;只需几个步骤即可搭建一个可用的私服&#xff0c;但因为公司系统环境&#xff08;测试&#xff09;的原因还是遇到几个…

Effeckt.css – CSS3 Transitions Animations 精妙应用

CSS3 不仅仅用作界面效果补充&#xff0c;还可以用来替换传统的一些界面实现&#xff0c;而且效果更酷。Effeckt.css 收集了众多精妙的 CSS3 Transitions & Animations 效果应用&#xff0c;例如&#xff1a;弹窗、按钮、导航、列表、页面切换等等。 您可能感兴趣的相关文章…

Java开发人员的5种工具

改善我们编写的Java代码的一种方法是使用最好的工具。 因此&#xff0c;让我们看看IDR Solutions建议使用的5种最常用的工具来帮助Java开发人员编写更好的代码。 查找错误 FindBugs是一个开放源代码程序&#xff0c;根据Lesser GNU Public许可的条款进行分发&#xff0c;并且使…

html如何让字体自动变色,CSS使文字部分变色

思路思路很简单&#xff0c;就是一个字写两遍&#xff0c;一个字只显示部分&#xff0c;不过不能真的把一个字写两遍。这里就需要用到CSS伪元素:before和:after&#xff0c;记住这个“伪元素”的“伪”字&#xff0c;表明它本来是不存在的。我们的方法就是在伪元素里放置相同的…

打开回收站

explorer.exe ::{645FF040-5081-101B-9F08-00AA002F954E}转载于:https://www.cnblogs.com/hshy/p/9877429.html

Codrops 教程:基于 CSS3 的精美模态窗口效果

Codrops 分享了漂亮的模态窗口效果实现方法&#xff0c;希望给前端开发人员提供一些创新显示对话框的启示。这个方案使用了触发按钮&#xff08;或任何的 HTML 元素&#xff09;&#xff0c;在点击的时候出现一个模态窗口&#xff0c;带有简单的过渡&#xff08;或动画&#xf…

html5录像功能限制时间,HTML5拍照和摄像机功能实战详解

开篇最近在做一个chrome app的云相机应用&#xff0c;应用包括拍照、摄像、保存照片视频、上传文件等等核心功能&#xff0c;其中涉及到很多HTML5对媒体流相关的API。写这篇文章的目的&#xff0c;其一是总结梳理知识点&#xff0c;最重要是希望对有相关需求的读者提供一些指导…

选择Vert.x的3个理由

Vert.x是用于JVM的轻量级高性能应用程序平台 现代Web应用程序和移动客户端的兴起重新定义了Web服务器的期望。 Node.js是第一个识别范式转换并提供解决方案的技术。 应用程序平台Vert.x借鉴了Node.js的一些创新&#xff0c;并使其在JVM上可用&#xff0c;将新鲜的想法与最复杂…

spring和mybatis的整合开发(基于MapperScannerConfigurer的整合开发(适用于复杂项目,接口较多的情况))...

在实际项目中&#xff0c;Dao层会包含很多接口&#xff0c;这样会导致spring配置文件过于臃肿。这时就需要采用扫描包的形式来配置mybaits中的映射器。 采用MapperScannerConfigurer来实现。 MapperScannerConfigurer类在spring配置文件中可以配置以下几个属性&#xff1a; 1.b…

页面加载前执行函数

window.onpageshow function(){ var token gg.core.cookie.getValue("token"); console.log(token); if(token ""||token null){ window.location login.html; return; }};转载于:https://www.cnblogs.com/tuhazi/p/987825…

表格对角线两边打字_表格斜线一分为二怎么打字(excel斜杠分割表格打字)

在整理表格的时候&#xff0c;相信许多朋友都会涉及到表格斜线的制作。比如单斜线和双斜线来区分不同维度项目。下面我们就来学习一下&#xff0c;如何通过Excel快速来添加我们的表格斜线。案例一&#xff1a;两步快速制作单表格单斜线第一步&#xff1a;首先在单元格中依次输入…