1. Introduction
Kotlin is a programming language for the JVM and compiles directly to Java Bytecode. However, it’s a lot more concise than Java, and certain JVM features don’t directly fit into the language.
Instead, Kotlin provides a set of annotations that we can apply to our code to trigger these features. These all exist in the kotlin.jvm package within kotlin-stdlib.
One of the more esoteric of these is the @JvmSynthetic annotation.
2. What Does @JvmSynthetic Do?
This annotation is applicable to methods, fields, getters, and setters — and it marks the appropriate element as synthetic in the generated class file.
We can use this annotation in our code exactly the same as any other annotation:
@JvmSynthetic val syntheticField: String = "Field" var syntheticAccessor: String @JvmSynthetic get() = "Accessor" @JvmSynthetic set(value) { } @JvmSynthetic fun syntheticMethod() { }
When the above code is compiled, the compiler assigns the ACC_SYNTHETIC attribute to the corresponding elements in the class file:
private final java.lang.String syntheticField; descriptor: Ljava/lang/String; flags: ACC_PRIVATE, ACC_FINAL, ACC_SYNTHETIC ConstantValue: String Field RuntimeInvisibleAnnotations: 0: #9() public final void syntheticMethod(); descriptor: ()V flags: ACC_PUBLIC, ACC_FINAL, ACC_SYNTHETIC Code: stack=0, locals=1, args_size=1 0: return LocalVariableTable: Start Length Slot Name Signature 0 1 0 this Lcom/baeldung/kotlin/SyntheticTest; LineNumberTable: line 20: 0
3. What Is the Synthetic Attribute?
The ACC_SYNTHETIC attribute is intended by the JVM Bytecode to indicate that an element wasn’t actually present in the original source code, but was instead generated by the compiler.
Its original intent was to support nested classes and interfaces in Java 1.1, but now we can apply it to any elements we may need it for.
Any element that the compiler marks as synthetic will be inaccessible from the Java language. This includes not being visible in any tooling, such as our IDE. However, our Kotlin code has no such restrictions and can both see and access these elements perfectly fine.
Note that if we have a Kotlin field annotated with @JvmSynthetic but not annotated with @JvmField, then the generated getter and setter are not considered synthetic methods and can be accessed just fine.
We can access synthetic elements from Java using the Reflection API if we’re able to locate them — for example, by name:
Method syntheticMethod = SyntheticClass.class.getMethod("syntheticMethod"); syntheticMethod.invoke(syntheticClass);
4. What Can I Use This For?
The only real benefits of this are hiding code from Java developers and tools, and as an indication to other developers as to the state of the code. It’s intended for working at a much lower level than most typical application code.
The intention of it is to support code generation, allowing the compiler to generate fields and methods that shouldn’t be exposed to other developers but that are needed to support the actual exposed interface. We can think of it as a level of protection beyond private or internal.
Alternatively, we can use it to hide code from other tools, such as code coverage or static analysis.
However, there’s no guarantee that any given tool will honor this flag, so it might not always be useful here.
5. Conclusion
The @JvmSynthetic annotation may not be the most useful tool available, but it does have uses in certain situations, as we’ve seen here.
As always, even though we may only rarely use it, another tool available in your developer toolbox can be quite beneficial. When the time comes that you have a need for this tool, it’s well worth knowing how it works.