147

什么是 Java 中的合成类?为什么要使用它?我该如何使用它?

4

13 回答 13

111

Java 具有在运行时创建类的能力。这些类称为合成类或动态代理。

有关详细信息,请参阅http://java.sun.com/j2se/1.5.0/docs/guide/reflection/proxy.html

其他开源库,例如CGLIBASM也允许您生成合成类,并且比 JRE 提供的库更强大。

合成类被 Spring AOP 和 AspectJ 等 AOP(面向方面​​编程)库以及 Hibernate 等 ORM 库使用。

于 2008-12-30T10:58:23.760 回答
54

好吧,我在谷歌上找到了第一个问题的答案:

如果一个类是由编译器生成的,那么它可能会被标记为合成类,也就是说,它不会出现在源代码中。

这只是一个基本定义,但我在论坛帖子中找到了它并且没有解释。还在寻找更好的...

于 2008-12-30T04:49:10.887 回答
15

例如,当您有一个 switch 语句时,java 会创建一个以 $ 开头的变量。如果您想查看一个示例,请查看其中包含 switch 语句的类的 java 反射。当您在类中的任何位置至少有一个 switch 语句时,您将看到这些变量。

要回答您的问题,我认为您无法访问(反射除外)合成类。

如果您正在分析一个您对(通过反射)一无所知的类并且需要了解关于该类的非常具体和低级的事情,那么您最终可能会使用与合成类有关的 Java 反射方法。这里唯一的“用途”是获取有关该类的更多信息,以便在您的代码中适当地使用它。

(如果您这样做,您可能正在构建其他开发人员可以使用的某种框架。)

否则,如果您不使用反射,那么我所知道的合成类没有实际用途。

于 2008-12-31T04:52:10.537 回答
15

合成类/方法/字段:

这些东西对虚拟机很重要。看看下面的代码片段:

class MyOuter {

  private MyInner inner;

  void createInner() {
    // The Compiler has to create a synthetic method
    // to construct a new MyInner because the constructor
    // is private.
    // --> synthetic "constructor" method
    inner = new MyInner();

    // The Compiler has to create a synthetic method
    // to doSomething on MyInner object because this
    // method is private.
    // --> synthetic "doSomething" method
    inner.doSomething();
  }

  private class MyInner {
    // the inner class holds a syntetic ref_pointer to
    // the outer "parent" class
    // --> synthetic field
    private MyInner() {
    }
    private void doSomething() {
    }
  }
}
于 2009-08-11T13:52:00.340 回答
8

根据这个讨论,尽管语言规范描述了类的“isSynthetic”属性,但实现几乎忽略了这一点,并且不用于动态代理或匿名类。合成字段和构造函数用于实现嵌套类(在字节码中没有嵌套类的概念,只有在源代码中)。

我认为综合类的概念被简单地证明是没有用的,即没有人关心一个类是否是综合的。对于字段和方法,它可能只用在一个地方:确定在 IDE 类结构视图中显示什么 - 您希望正常方法和字段显示在那里,而不是用于模拟嵌套类的合成方法和字段。OTOH,您确实希望匿名课程出现在那里。

于 2008-12-30T13:22:31.893 回答
6

它们由 JVM 在运行时调用内部类的私有成员以进行调试时创建

JVM 在运行时为其执行目的创建的方法、字段、类称为 Synthetic

http://www.javaworld.com/article/2073578/java-s-synthetic-methods.html

http://javapapers.com/core-java/java-synthetic-class-method-field/

于 2014-08-07T07:05:34.400 回答
4

正如各种答案已经指出的那样,允许编译器生成不直接对应于源代码中的某些内容的各种构造(包括类)。这些必须标记为合成:

13.1. 二进制的形式

类或接口的二进制表示还必须包含以下所有内容:
[...]
11. 如果 Java 编译器发出的构造与源代码中显式或隐式声明的构造不对应,则必须将其标记为合成,除非发出的构造是类初始化方法(JVMS §2.9)。
[...]

正如@Holger在对另一个问题的评论中指出的那样,此类构造的相关示例是表示方法引用和 lambda 的 Class 对象:

System.out.println(((Runnable) System.out::println).getClass().isSynthetic());
System.out.println(((Runnable) () -> {}).getClass().isSynthetic());

输出:

true
true

虽然没有明确提及,但它来自 15.27.4。Lambda 表达式的运行时评估

lambda 表达式的值是对具有以下属性的类实例的引用:[...]

以及方法参考的几乎相同的措辞(15.13.3. 方法参考的运行时评估)。

由于该类在源代码的任何地方都没有明确提及,因此它必须是合成的。

于 2018-11-22T10:53:38.260 回答
4

什么是 Java 中的合成类?

synthetic是由Java 编译器.class生成的文件,它在源代码中不存在。

类的示例用法synthetic匿名内部类

  • java.text.DigitList$1是一个类,它是java.text.DigitListsynthetic内部的一个匿名内部类
  • 并且没有名为 like 的源代码文件,DigitList$1.java但它是一个内部文件DigitList.java

为什么要使用它?

它是 Java 编译器逻辑内部生成.class文件的一种机制

我该如何使用它?

不,开发人员不直接使用它。

Java编译器使用synthetic生成.class文件,然后JVM读取.class文件执行程序逻辑。

更多细节

  • 这篇文章详细解释synthetic
  • 链接列出了JDKsynthetic中的所有类出口
于 2019-10-23T06:26:29.843 回答
3

EasyMock 还使用合成类或动态代理在运行时创建接口或抽象类的实现。

http://www.easymock.org/

于 2008-12-30T13:04:57.797 回答
2

当 Java 编译器编译某些结构(例如内部类)时,它会创建合成结构;这些是在源代码中没有相应构造的类、方法、字段和其他构造。
用途: 综合构造使 Java 编译器能够实现新的 Java 语言功能,而无需更改 JVM。但是,合成结构在不同的 Java 编译器实现之间可能会有所不同,这意味着 .class 文件也可以在不同的实现之间有所不同。
参考:docs.oracle.com

于 2014-06-17T19:35:27.770 回答
1

如果我猜对了,合成类是动态生成的,无需给它一个明确的名称。例如:

//...
Thread myThread = new Thread() {
         public void run() {
           // do something ...
         }
       };
myThread.start();
//...

这将创建 Thread 的合成子类并覆盖其 run() 方法,然后实例化并启动它。

于 2011-06-23T10:50:00.687 回答
1

合成类不会出现在您的代码中:它是由编译器组成的。例如,Java 中的编译器组成的桥接方法通常是合成的。

public class Pair<T> {
    private T first;
    private T second;
    public void setSecond(T newValue) {
        second = newValue;
    }
}


public class DateInterval extends Pair<String> {
    public void setSecond(String second) {
        System.out.println("OK sub");
    }

    public static void main(String[] args) throws NoSuchFieldException, SecurityException {
        DateInterval interval = new DateInterval();
        Pair pair = interval;
        pair.setSecond("string1");
    }
}

使用命令javap -verbose DateInterval,可以看到桥接方法:

public void setSecond(java.lang.Object);
flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC

这是由编译器组成的;它不会出现在您的代码中。

于 2015-04-04T03:46:40.257 回答
0

综合构造是在源代码中没有相应构造的类、方法、字段等。综合构造使 Java 编译器能够在不更改 JVM 的情况下实现新的 Java 语言特性。但是,合成结构在不同的 Java 编译器实现之间可能会有所不同,这意味着 .class 文件也可以在不同的实现之间有所不同。

于 2015-02-02T16:16:54.427 回答