简体   繁体   English

Java中的“真实”和“合成”方法参数是什么?

[英]What are 'real' and 'synthetic' Method parameters in Java?

Looking into jlrExecutable class I've found a method called hasRealParameterData() and from its name and code context I assume that it tells whether a particular method has 'real' or 'synthetic' params.查看jlrExecutable类,我发现了一个名为hasRealParameterData()的方法,从它的名称和代码上下文中,我假设它告诉特定方法是否具有“真实”或“合成”参数。

If I take eg method Object.wait(long, int) and call hasRealParameterData() it turns out that it returns false which is confusing to me, as the method is declared in Object class along with its params.如果我采用例如方法Object.wait(long, int)并调用hasRealParameterData() ,结果证明它返回false ,这让我感到困惑,因为该方法是在Object类及其参数中声明的。

From this I've got a couple of questions:从这里我有几个问题:

  1. What are 'real' and 'synthetic' Method parameters and why Java believes that params of Object.wait(long, int) are not 'real'?什么是“真实”和“合成”方法参数,为什么 Java 认为Object.wait(long, int)的参数不是“真实的”?
  2. How can I define a method with 'real' params?如何定义具有“真实”参数的方法?

Preamble - don't do this.序言 - 不要这样做。

As I mentioned in the comments as well: This is a package private method.正如我在评论中提到的那样:这是一个包私有方法。 That means:这意味着:

[A] It can change at any time, and code built based on assuming it is there will need continuous monitoring; [A] 它可以随时更改,基于假设它存在构建的代码需要持续监控; any new java release means you may have to change things.任何新的 Java 版本都意味着您可能需要进行更改。 You probably also need a framework if you want your code to be capable of running on multiple different VM versions.如果您希望您的代码能够在多个不同的 VM 版本上运行,您可能还需要一个框架。 Maybe it'll never meaningfully change, but you have no guarantee so you're on the hook to investigate each and every JVM version released from here on out.也许它永远不会有意义地改变,但你不能保证,所以你必须调查从这里发布的每一个 JVM 版本。

[B] It's undocumented by design. [B] 它在设计上没有记录。 It may return weird things.它可能会返回奇怪的东西。

[C] The java module system restriction stuff is getting tighter every release; [C] java 模块系统限制的东西每次发布都越来越严格; calling this method is hard, and will become harder over time.调用这个方法很困难,并且随着时间的推移会变得更难。

Whatever made you think this method is the solution to some problem you're having - unlikely.无论您如何认为这种方法可以解决您遇到的某些问题 - 不太可能。 If it does what you want at all, there are probably significantly better solutions available.如果它完全符合您的要求,那么可能有更好的解决方案可用。 I strongly advise you take one step backwards and ask a question about the problem you're trying to solve, instead of asking questions about this particular solution you've come up with.我强烈建议您退后一步,就您要解决的问题提出问题,而不是就您提出的这个特定解决方案提出问题。

Having gotten that out of the way...解决了这个问题...

Two different meanings两种不同的意思

The problem here is that 'synthetic' means two utterly unrelated things and the docs are interchanging the meaning.这里的问题是“综合”意味着两个完全不相关的东西,文档正在互换含义。 The 4 unrelated meanings here are:这里的4个不相关的含义是:

  • SYNTHETIC , the JVM flag. SYNTHETIC ,JVM 标志。 This term is in the JLS.该术语在 JLS 中。
  • 'real', a slang term used to indicate anything that is not marked with the JVM SYNTETHIC flag. 'real',一个俚语术语,用于表示任何未标记 JVM SYNTETHIC 标志的东西。 This term is, as far as I know, not official.据我所知,这个术语不是官方的。 There isn't an official term other than simply 'not SYNTHETIC'.除了简单的“非合成”之外,没有任何官方术语。
  • Synthetic, as in, the parameter name (and other data not guaranteed to be available in class files) are synthesised.综合,如参数名称(以及不保证在类文件中可用的其他数据)被综合。
  • Real, as in, not the previous bullet point's synthetic.真实的,例如,不是上一个要点的合成。 The parameter is fully formed solely on the basis of what the class file contains.该参数完全基于类文件包含的内容而形成。

The 'real' in hasRealParameterData is referring to the 4th bullet, not the second. hasRealParameterData 中的“真实”指的是第四个项目符号,而不是第二个项目符号。 But, all 4 bullet point meanings are used in various comments in the Executable.java source file!但是,在 Executable.java 源文件的各种注释中都使用了所有 4 个要点的含义!

The official meaning - the SYNTHETIC flag官方含义 - SYNTHETIC flag

The JVM has the notion of the synthetic flag. JVM 有合成标志的概念。

This means it wasn't in the source code but javac had to make this element in order to make stuff work.这意味着它不在源代码中,但javac必须制作这个元素才能使东西正常工作。 This is done to paper over mismatches between java-the-language and java-the-VM-definition, as in, differences between .java and .class .这样做是为了弥补 java-the-language 和 java-the-VM-definition 之间的不匹配,例如.java.class之间的差异。 Trivial example: At least until the nestmates concept, the notion of 'an inner class' simply does not exist at the class file level.简单的例子:至少在嵌套的概念之前,“内部类”的概念根本不存在于类文件级别。 There is simply no such thing.根本没有这样的事情。 Instead, javac fakes it: It turns:相反, javac伪造它:它变成:

class Outer {
  private static int foo() {
    return 5;
  }

  class Inner {
    void example() {
      Outer.foo();
    }
  }
}

Into 2 seemingly unrelated classes, one named Outer , and one named Outer$Inner , literally like that.分成 2 个看似无关的类,一个名为Outer ,一个名为Outer$Inner ,字面意思就是这样。 You can trivially observe this: Compile the above file and look at that - 2 class files, not one.您可以轻松地观察到这一点:编译上面的文件并查看 - 2 个类文件,而不是一个。

This leaves one problem: The JLS claims that inner classes get to call private members from their outer class.这留下了一个问题:JLS 声称内部类可以从外部类调用private成员。 However, at the JVMS (class file) level, we turned these 2 classes into separate things, and thus, Outer$Inner cannot call foo .但是,在 JVMS(类文件)级别,我们将这两个类变成了独立的东西,因此Outer$Inner不能调用foo Now what?怎么办? Well, javac generates a 'bridger' method.那么, javac会生成一个“桥接器”方法。 It basically compiles this instead:它基本上是编译这个:

class Outer {
  private static int foo() {
    return 5;
  }

  /* synthetic */ static int foo$() {
    return foo();
  }
}

class Outer$Inner {
  private /* synthetic */ Outer enclosingInstance;

  void example() {
    Outer.foo$();
  }
}

The JVM can generate fields, extra overload methods (for example, if you write class MyClass implements List<String> {} , you will write eg add(String x) , but .add(Object x) still needs to exist to cater to erasure - that method is generated by javac , and will be marked with the SYNTHETIC modifier. JVM 可以生成字段,额外的重载方法(例如,如果你写class MyClass implements List<String> {} ,你会写如add(String x) ,但是.add(Object x)仍然需要存在才能迎合擦除 - 该方法由javac生成,并将使用 SYNTHETIC 修饰符进行标记。

One effect of the SYNTHETIC modifier is that javac acts as if these methods do not exist. SYNTHETIC 修饰符的一个效果是javac的行为就像这些方法不存在一样。 If you attempt to actually write Outer.foo$() in java code, it won't compile, javac will act as if the method does not exist.如果您尝试在 java 代码中实际编写Outer.foo$() ,它将无法编译,javac 将表现得好像该方法不存在一样。 Even though it does.即使确实如此。 If you use bytebuddy or a hex editor to clear that flag in the class file, then javac will compile that code just fine.如果您使用 bytebuddy 或十六进制编辑器清除类文件中的该标志,那么 javac 将编译该代码就好了。

generating parameter names生成参数名称

Weirdly, perhaps, in the original v1.0 Java Language Spec, parameter types were, obviously, a required part of a method's signature and are naturally encoded in class files.奇怪的是,在最初的 v1.0 Java 语言规范中,参数类型显然是方法签名的必需部分,并且自然地编码在类文件中。 You can write this code: Integer.class.getMethods();您可以编写以下代码: Integer.class.getMethods(); , loop through until you find the static parseInt method, and then ask the jlrMethod instance about its parameter type, which will dutifully report: the first param's type is String . , 循环遍历直到找到静态的parseInt方法,然后向jlrMethod实例询问它的参数类型,它会忠实地报告:第一个参数的类型是String You can even ask it for its annotations.您甚至可以要求它提供注释。

But weirdly enough as per JLS 1.0 you cannot ask for its name - simply because it is not there , there was no actual need to know it, it does take up space, java wanted to be installed on tiny devices (I'm just guessing at the reasons here), so the info is not there.但奇怪的是,根据 JLS 1.0,你不能问它的名字——仅仅因为它不存在,实际上不需要知道它,它确实占用空间,java 想安装在微型设备上(我只是在猜测在此处的原因),因此信息不存在。 You can add it - as debug info, via the -g parameter, because having the names of things is convenient.您可以通过-g参数将它添加为调试信息,因为拥有事物的名称很方便。

However, in later days this was deemed too annoying, and more recently compilers DO stuff the param name in a class file.然而,在后来的日子里,这被认为太烦人了,最近编译器确实将参数名称填充到类文件中。 Even if you do not use the -g param to 'include debug symbol info'.即使您不使用-g参数来“包含调试符号信息”。

Which leaves one final question: java17 can still load classes produced by javac 1.1 .这留下了最后一个问题:java17 仍然可以加载由javac 1.1生成的类。 So what is it supposed to do when you ask for the name of param1 of such a method?那么当你询问这种方法的 param1 的名称时,它应该做什么呢? The name simply cannot be figured out, it simply isn't there in the class file.该名称根本无法弄清楚,它根本不存在于类文件中。 It can fall back to looking at the debug symbol table (and it does), but if that isn't there - then you're just out of luck.它可以回退到查看调试符号表(确实如此),但如果那不存在 - 那么你只是不走运。

What the JVM does is make that name arg0 , arg1 , etc. You may have seen this in decompiler outputs. JVM 所做的就是将该名称命名为arg0arg1等。您可能已经在反编译器输出中看到了这一点。

THAT is what the hasRealParameterData() method is referring to as 'real' - arg0 is 'synthesized', and in contrast, foo (the actual name of the param) is 'real'.这就是hasRealParameterData hasRealParameterData()方法所指的“真实” - arg0是“合成的”,相反, foo (参数的实际名称)是“真实的”。

So how would one have a method that has 'real' data in that sense (the 4th bullet)?那么,如何拥有一种在这种意义上具有“真实”数据的方法(第 4 条)? Simply compile it, it's quite hard to convince a modern java compiler to strip all param names.简单地编译它,很难说服现代 Java 编译器去除所有参数名称。 Some obfuscators do this.一些混淆器会这样做。 You can compile with a really old -target and definitely don't add -g , and you'll probably get non-real, as per hasRealParameterData() .您可以使用非常旧的-target进行编译,并且绝对不要添加-g ,并且根据hasRealParameterData() ,您可能会得到非真实的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM