什么是@JvmSynthetic在Kotlin中的用途?

时间:2016-12-07 16:15:26

标签: java kotlin java-interop synthetic

我在kotlin-stdlib中遇到了@JvmSynthetic注释,我想知道它是什么,但不幸的是,它没有记录。

据我所知,将它应用于程序元素会将synthetic修饰符添加到相应的字节码元素中。因此,该元素在Java中变得不可见:

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

Java代码中的某处:

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

但在Kotlin代码中仍然可以看到相同的元素:

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

隐藏来自非Kotlin来源的声明是否有效使用@JvmSynthetic?它是预期用途吗?其他适当的用例是什么?

由于@JvmSynthetic隐藏了Java中的函数,因此它们也无法在Java中被覆盖(当涉及abstract成员时,调用会产生AbstractMethodError)。鉴于此,我可以使用@JvmSynthetic禁止在Java源代码中覆盖Kotlin类的成员吗?

2 个答案:

答案 0 :(得分:12)

在普通Java中,synthetic编译器生成javac方法。通常,当封闭类访问使用private修饰符指定的字段时,编译器必须在嵌套类上创建合成方法。

给出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!";
    }
}

SyntheticSample类访问nested.syntheticString字段时,它确实调用了编译器生成的静态synthetic方法(名称类似于access$100)。

即使Kotlin公开了@JvmSynthetic注释,也能够强制使用#34;合成方法的创建,我建议不要在普通的"用户"码。合成方法是编译器制作的低级技巧,我们不应该在日常代码中依赖这些东西。我认为它可以支持标准库的其他部分,但是你应该直接询问JetBrains的人是否有点好奇(试试官方Kotlin Discussion Forum

答案 1 :(得分:3)

首先,要回答实际上是什么合成方法,让我们看一下Java language specification

  

11。如果Java编译器发出的结构与源代码中显式或隐式声明的结构不对应,则必须将其标记为 synthetic 。构造是一个类初始化方法(JVMS§2.9)。

@JvmSynthetic注释确实做到了这一点:阻止从源代码访问。该方法仍会反射显示,然后标记为合成。

更准确地说,来自the Kotlin documentation(重点是我):

  

@JvmSynthetic

     

在Java字节码中带注释的目标上设置ACC_SYNTHETIC标志。

     

合成目标在编译时对Java源代码不可访问,而Kotlin源仍然可访问。将目标标记为“ synthetic”是二进制兼容的更改,已经编译的Java代码将能够访问该目标。

     

此注释用于稀有情况,当API设计人员需要从Java API中隐藏Kotlin特定的目标,同时又将其保留为Kotlin API的一部分,因此生成的API对于这两种情况都是惯用的。

如上一段所述,@JvmSynthetic是用于API设计的工具,它使库编写者可以避免自动生成Java等效项。可能最流行的用例是仅Kotlin的功能,例如运算符重载,componentN()方法或属性,这些功能可能会以更惯用的方式在Java中公开。

值得注意的是,此注释的目标是属性设置器/获取器,函数和字段-基本上所有将Java转换为方法的东西。

@Target([
    AnnotationTarget.FUNCTION,
    AnnotationTarget.PROPERTY_GETTER,
    AnnotationTarget.PROPERTY_SETTER,
    AnnotationTarget.FIELD])
annotation actual class JvmSynthetic