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.