A synthetic element is any element that is present in a compiled class file but not in the source code it is compiled from. By checking an element for it being synthetic, you allow a distinction of such elements for tools that process code reflectively. This is of course first of all relevant to libraries that use reflection but it is also relevant for other tools like IDEs that do not allow you to call synthetic methods or to work with synthetic classes. Finally, it is also important for the Java compiler to verify code during its compilation to never directly use synthetic elements. Synthetic elements are only used to make the Java runtime happy which simply processes (and verifies) the delivered code where it treats synthetic elements identically to any other element.
You already mentioned inner classes as an example where synthetic elements are inserted by the Java compiler, so let us look at such a class:
class Foo {
private String foo;
class Bar {
private Bar() { }
String bar() {
return foo;
}
}
Bar bar() {
return new Bar();
}
}
This compiles perfectly fine but without synthetic elements, it would be refused by a JVM that does not know a thing about inner classes. The Java compiler desugares the above class to something like the following:
class Foo {
private String foo;
String access$100() { // synthetic method
return foo;
}
Foo$Bar bar() {
return new Foo$Bar(this, (Foo$1)null);
}
Foo() { } // NON-synthetic, but implicit!
}
class Foo$Bar {
private final Foo $this; // synthetic field
private Foo$Bar(Foo $this) { // synthetic parameter
this.$this = $this;
}
Foo$Bar(Foo $this, Foo$1 unused) { // synthetic constructor
this($this);
}
String bar() {
return $this.access$100();
}
}
class Foo$1 { /*empty, no constructor */ } // synthetic class
As said, the JVM does not know about inner classes but enforces private access of members, i.e. an inner class would not be able to access its enclosing classes' private properties. Thus, the Java compiler needs to add so-called accessors to an accessed class in order to expose its non-visible properties:
The foo
field is private and can therefore only be accessed from within Foo
. The access$100
method exposes this field to its package in which an inner class is always to be found. This method is synthetic as it is added by the compiler.
The Bar
constructor is private and can therefore only be called from within its own class. In order to instantiate an instance of Bar
, another (synthetic) constructor needs to expose the construction of an instance. However, constructors have a fixed name (internally, they are all called <init>
), thus we cannot apply the technique for method accessors where we simply named them access$xxx
. Instead, we make constructor accessors unique by creating a synthetic type Foo$1
.
In order to access its outer instance, an inner class needs to store a reference to this instance which is stored in a synthetic field $this
. This reference needs to be handed to the inner instance by a synthetic parameter in the constructor.
Other examples for synthetic elements are classes that represent lambda expressions, bridge methods when overriding methods with a type-divergent signatures, the creation of Proxy
classes or classes that are created by other tools like Maven builds or runtime code generators such as Byte Buddy (shameless plug).