What's the intended use of @JvmSynthetic in Kotlin?
Asked Answered
W

2

21

I have come across the @JvmSynthetic annotation in kotlin-stdlib, and I'm wondering what it is for, but, unfortunately, it is undocumented. (UPD: it was at that moment)

As far as I understand, applying it to a program element will add the synthetic modifier to the corresponding bytecode elements. As a consequence, the element becomes invisible from Java:

class MyClass {
    @JvmSynthetic
    fun f() { }
}

Somewhere in Java code:

MyClass c = new MyClass();
c.f() // Error: cannot resolve method f()

But the same elements are still visible in Kotlin code:

val c = MyClass()
c.f() // OK

Is hiding declarations from non-Kotlin sources a valid use of @JvmSynthetic? Is it the intended use? What are the other appropriate use cases?

Since @JvmSynthetic hides functions from Java, they cannot be overridden in Java either (and when it comes to an abstract member, the calls then result into AbstractMethodError). Given that, can I use @JvmSynthetic to prohibit overriding members of a Kotlin class in Java sources?

Weinberg answered 7/12, 2016 at 16:15 Comment(1)
I use it to hide declarations from Java, especially for those which are @PublishedApi internal, i. e. accessible in inline functions.Ambriz
P
7

First, to answer what synthetic methods actually are, let's have a look at the Java language specification:

11. A construct emitted by a Java compiler must be marked as synthetic if it does not correspond to a construct declared explicitly or implicitly in source code, unless the emitted construct is a class initialization method (JVMS §2.9).

The @JvmSynthetic annotation does exactly that: prevent access from source code. The method will still appear in reflection and is then marked as synthetic.

More precisely, from the Kotlin documentation (emphasis mine):

@JvmSynthetic

Sets ACC_SYNTHETIC flag on the annotated target in the Java bytecode.

Synthetic targets become inaccessible for Java sources at compile time while still being accessible for Kotlin sources. Marking target as synthetic is a binary compatible change, already compiled Java code will be able to access such target.

This annotation is intended for rare cases when API designer needs to hide Kotlin-specific target from Java API while keeping it a part of Kotlin API so the resulting API is idiomatic for both.

As described in the last paragraph, @JvmSynthetic is a tool for API design, which lets a library writer avoid automatic generation of Java equivalents. Probably the most popular use cases are Kotlin-only features, such as operator overloading, componentN() methods or properties, which may have a more idiomatic way to be exposed in Java.

It is noteworthy that the target of this annotations are property setters/getters, functions and fields -- basically everything that translates in Java to a method.

@Target([
    AnnotationTarget.FUNCTION,
    AnnotationTarget.PROPERTY_GETTER,
    AnnotationTarget.PROPERTY_SETTER,
    AnnotationTarget.FIELD])
annotation actual class JvmSynthetic
Polypody answered 19/9, 2018 at 13:5 Comment(0)
C
12

In plain Java, synthetic methods are generated by the javac compiler. Normally the compiler must create synthetic methods on nested classes, when fields specified with the private modifier are accessed by the enclosing class.

Given the following class in java:

public final class SyntheticSample
{
    public static void main(final String[] args)
    {
        SyntheticSample.Nested nested = new SyntheticSample.Nested();
        out.println("String: " + nested.syntheticString);
    }

    private static final class Nested
    {
        private String syntheticString = "I'll become a method!";
    }
}

when the SyntheticSample class accesses the nested.syntheticString field, it is indeed calling a static synthetic method generated by the compiler (named something like access$100).

Even if Kotlin exposes a @JvmSynthetic annotation that is able to "force" the creation of synthetic methods, I advice to not using it in normal "user" code. Synthetic methods are low-level tricks made by the compiler, and we should never rely on such things in everyday code. I think it's there to support other parts of the standard library, but you should ask the JetBrains guys directly if you're curious (try on the official Kotlin Discussion Forum)

Cardiac answered 7/12, 2016 at 18:53 Comment(1)
Well, I agree it looks like a dangerous tool that needs careful handling, but still it's a part of kotlin-stdlib, it's a public API. It makes me think there should be use cases for @JvmSynthetic which an ordinary developer (or maybe a library/tools developer) may encounter. Also, synthetic methods are used broader by javac then just for nested classses: at least for overriding a method with a different signature (including generic methods overridden with non-generic ones).Weinberg
P
7

First, to answer what synthetic methods actually are, let's have a look at the Java language specification:

11. A construct emitted by a Java compiler must be marked as synthetic if it does not correspond to a construct declared explicitly or implicitly in source code, unless the emitted construct is a class initialization method (JVMS §2.9).

The @JvmSynthetic annotation does exactly that: prevent access from source code. The method will still appear in reflection and is then marked as synthetic.

More precisely, from the Kotlin documentation (emphasis mine):

@JvmSynthetic

Sets ACC_SYNTHETIC flag on the annotated target in the Java bytecode.

Synthetic targets become inaccessible for Java sources at compile time while still being accessible for Kotlin sources. Marking target as synthetic is a binary compatible change, already compiled Java code will be able to access such target.

This annotation is intended for rare cases when API designer needs to hide Kotlin-specific target from Java API while keeping it a part of Kotlin API so the resulting API is idiomatic for both.

As described in the last paragraph, @JvmSynthetic is a tool for API design, which lets a library writer avoid automatic generation of Java equivalents. Probably the most popular use cases are Kotlin-only features, such as operator overloading, componentN() methods or properties, which may have a more idiomatic way to be exposed in Java.

It is noteworthy that the target of this annotations are property setters/getters, functions and fields -- basically everything that translates in Java to a method.

@Target([
    AnnotationTarget.FUNCTION,
    AnnotationTarget.PROPERTY_GETTER,
    AnnotationTarget.PROPERTY_SETTER,
    AnnotationTarget.FIELD])
annotation actual class JvmSynthetic
Polypody answered 19/9, 2018 at 13:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.