There is a bunch of languages running on the JVM, from of course Java, to Clojure and JRuby. All of them have different syntaxes, but it’s awesome they all compile to the same bytecode. The JVM unites them all. Of course, it’s biased toward Java, but even in Java, there is some magic happening in the bytecode.
The most well-known trick comes from the following code:
public class Foo {
static class Bar {
private Bar() {}
}
public static void main(String... args) {
new Bar();
}
}
Can you guess how many constructors the Bar
class has?
Two.
Yes, you read that well.
For the JVM, the Bar
class declares 2 constructors.
Run the following code if you don’t believe it:
Class<Foo.Bar> clazz = Foo.Bar.class;
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
System.out.println(constructors.length);
Arrays.stream(constructors).forEach(constructor -> {
System.out.println("Constructor: " + constructor);
});
The output is the following:
Constructor: private Foo$Bar() Constructor: Foo$Bar(Foo$1)
The reason is pretty well documented.
The bytecode knows about access modifiers, but not about nested classes.
In order for the Foo
class to be able to create new Bar
instances, the Java compiler generates an additional constructor with a default package visibility.
This can be confirmed with the javap
tool.
javap -v out/production/synthetic/Foo\$Bar.class
This outputs the following:
[...] { Foo$Bar(Foo$1); descriptor: (LFoo$1;)V flags: ACC_SYNTHETIC Code: stack=1, locals=2, args_size=2 0: aload_0 1: invokespecial #1 // Method "<init>":()V 4: return LineNumberTable: line 2: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this LFoo$Bar; 0 5 1 x0 LFoo$1; } [...]
Notice the ACC_SYNTHETIC
flag.
Going to the JVM specifications yields the following information:
The ACC_SYNTHETIC
flag indicates that this method was generated by a compiler and does not appear in source code, unless it is one of the methods named in §4.7.8.
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.6
Theoretically, it should be possible to call this generated constructor - notwithstanding the fact that it’s not possible to provide an instance of |
At this point, one could wonder why all the fuss about the synthetic flag. It was introduced in Java to resolve the issue of nested classes access. But other JVM languages use it to implement their specification. For example, Kotlin uses synthetic to access the companion object:
class Baz() {
companion object {
val BAZ = "baz"
}
}
Executing javap
on the .class
file returns the following output (abridged for readability purpose) :
{ public static final Baz$Companion Companion; descriptor: LBaz$Companion; flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL public Baz(); [...] public static final java.lang.String access$getBAZ$cp(); descriptor: ()Ljava/lang/String; flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC Code: stack=1, locals=0, args_size=0 0: getstatic #22 // Field BAZ:Ljava/lang/String; 3: areturn LineNumberTable: line 1: 0 RuntimeInvisibleAnnotations: 0: #15() } [...]
Notice the access$getBAZ$cp()
static method?
That’s the name of the method that should be called from Java:
public class FromJava {
public static void main(String... args) {
Baz.Companion.getBAZ();
}
}
Conclusion
While knowledge of the synthetic
flag is not required in the day-to-day work of a JVM developer, it can be helpful to understand some of the results returned by the reflection API.