Java记录反射与合成方法

gjmwrych  于 2023-02-02  发布在  Java
关注(0)|答案(2)|浏览(121)

基于旧版Java (7) Language Specifications (13.1.7)
Java编译器引入的任何在源代码中没有对应构造的构造都必须标记为合成,但默认构造函数、类初始化方法以及Enum类的values和valueOf方法除外。
在较新版本(Java (17) Language Specifications (13.1.7):)将措辞改为:
如果Java编译器发出的构造与源代码中显式或隐式声明的构造不对应,则必须将其标记为合成构造,除非发出的构造是类初始化方法(JVMS §2.9)。
我想知道这如何应用于为java Records (JEP 395)组件创建的accessor方法
例如

record ARecord(int a){}

我将有一个方法int a(),但没有表示这种方法的代码,根据旧JLS的措辞,这种方法是由编译器添加的,所以我希望它是合成的,但它不是,因为它可以通过在JShell上运行以下两行来证实

jshell
|  Welcome to JShell -- Version 17.0.1
|  For an introduction type: /help intro

jshell> record ARecord(int a){}
|  created record ARecord

jshell> ARecord.class.getDeclaredMethod("a").isSynthetic();
$2 ==> false

jshell>

我问这个问题的原因是因为我想使用反射(或运行时的任何其他编程方法)来确定类中的哪些元素具有匹配的代码结构,基本上那些元素具有表示它们的代码,这意味着:
对于以下代码

record ARecord(int a){

  pubic void someMethod() {}

}

该 * 实体 * 将有2个方法(asomeMethod),a没有表示它的代码,而someMethod有,我需要一种方法来根据该标准区分它们

cotxawn7

cotxawn71#

我想知道这是否是因为它被认为是隐式声明的,因为它的代码被隐式定义为组件的一部分
注意旧的规范只说“synthetic”应该标记在
在源代码中没有相应的构造
除了隐式声明的Enum.valuesEnum.valueOf。在那时,显然只有这两个隐式声明(在新规范使用短语的意义上)。:D
另一方面,新的规范说
不对应于在源代码中显式或隐式声明的构造***
注意,这种措辞自动地处理了Enum异常,但也处理了自那以后添加的大量隐式声明的内容,其中包括记录组件。
根据Java 17规范§8.10.3.记录成员,
此外,对于每个记录组件,记录类都有一个与记录组件同名的方法和一个空形参列表。这个方法可以显式或隐式声明,称为访问器方法。
...
如果一个记录类有一个记录组件,它的访问器方法没有显式声明,那么这个记录组件的访问器方法是隐式声明的[...]
方法a是在组件中隐式声明的,因此它不是合成的。
一般来说(可能有我不知道的例外),synthetic constructs是语言规范没有指定的结构,但是编译器的特定实现需要它来工作。规范基本上是说这样的结构必须在二进制文件中标记为“synthetic”。参见here的一些例子。

n9vozmp4

n9vozmp42#

类型中任何有synthetic标志的成员都会被javac**完全忽略。Javac的行为就像这些东西根本不存在一样。
因此,很明显,你为记录得到的“getter”不是合成的,如果它们是合成的,就不可能从.java代码中调用它们--调用它们的唯一方法是编写一个hacky javac克隆来编译对合成的访问,或者使用字节码操作来移除合成标志,或者直接发出字节码,或者使用反射。

相关问题