【问题标题】:What's the intended use of @JvmSynthetic in Kotlin?@JvmSynthetic 在 Kotlin 中的预期用途是什么?
【发布时间】:2017-04-22 16:20:10
【问题描述】:

我在 kotlin-stdlib 中遇到了 @JvmSynthetic 注释,我想知道它的用途,但不幸的是,它没有记录。 (UPD:就在那一刻)

据我了解,将其应用于程序元素会将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 类的成员吗?

【问题讨论】:

  • 我用它来隐藏 Java 的声明,尤其是那些@PublishedApi internal,即。 e.可在内联函数中访问。

标签: java kotlin java-interop synthetic


【解决方案1】:

在纯 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 注释,我还是建议不要在普通的“用户”代码中使用它。合成方法是编译器制作的低级技巧,我们不应该在日常代码中依赖这些东西。我认为它可以支持标准库的其他部分,但如果你好奇,你应该直接询问 JetBrains 人(试试官方Kotlin Discussion Forum)

【讨论】:

  • 好吧,我同意它看起来像一个需要小心处理的危险工具,但它仍然是kotlin-stdlib 的一部分,它是一个公共 API。这让我觉得应该有@JvmSynthetic 的用例,普通开发人员(或者可能是库/工具开发人员)可能会遇到这些用例。此外,synthetic 方法被 javac 广泛使用,然后仅用于嵌套类:至少用于覆盖具有不同签名的方法(包括用非泛型方法覆盖的泛型方法)。
【解决方案2】:

首先,回答一下合成方法究竟是什么,我们来看看Java language specification:

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

@JvmSynthetic 注释正是这样做的:防止从源代码访问。该方法仍会出现在反射中,然后标记为合成。

更准确地说,来自the Kotlin documentation(强调我的):

@JvmSynthetic

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

Java 源代码在编译时无法访问合成目标,但 Kotlin 源代码仍可访问。 将目标标记为合成目标是二进制兼容的更改,已编译的 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

【讨论】:

    猜你喜欢
    • 2012-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-09
    • 2019-09-17
    • 2019-11-24
    • 1970-01-01
    相关资源
    最近更新 更多