在Kotlin中使用接口的默认功能实现

Voj*_*ěch 30 kotlin

我有一个带有默认实现的Kotlin接口,例如:

interface Foo {
    fun bar(): String {
        return "baz"
    }
}
Run Code Online (Sandbox Code Playgroud)

在我尝试从Java实现此接口之前,这是可以的.当我这样做时,它表示该类需要被标记为抽象或实现该方法bar().此外,当我尝试实现该方法时,我无法调用super.bar().

zsm*_*b13 21

生成default可从Java调用的真实方法是Kotlin 1.2.40实验特征.

您需要使用注释注释方法@JvmDefault:

interface Foo {
    @JvmDefault
    fun bar(): String {
        return "baz"
    }
}
Run Code Online (Sandbox Code Playgroud)

默认情况下,此功能仍处于禁用状态,您需要将-Xjvm-default=enable标志传递给编译器才能使其正常工作.(如果您需要在Gradle中执行此操作,请参阅此处).

然而,它确实是实验性的.博客文章警告说,未来的设计和实现都可能会发生变化,至少在我的IDE中,尽管编译和工作正常,Java类仍然会因未实现这些方法而出现错误.

  • @Joachim Sauer:“@JvmDefault”注释现已弃用,有关详细信息,请参阅[答案](/sf/answers/4966953981/)。 (2认同)

Kar*_*ory 16

请参阅相关问题.

评论中有一条建议:

用Java编写接口(使用默认方法),Java和Kotlin类都正确使用这些默认值


Dan*_*cer 12

如果您知道不会在接口的任何实现中覆盖该函数,则可以使用扩展函数作为解决此问题的不错方法。只需将扩展功能放在与界面相同的文件中(并在顶层,以便其他文件可以使用它)。

例如,你正在做的事情可以这样完成:

interface Foo {
    // presumably other stuff
}

fun Foo.bar(): String {
    return "baz"
}
Run Code Online (Sandbox Code Playgroud)

有关它们的更多信息,请参阅有关扩展函数的文档

一个值得注意的“陷阱”:

我们想强调的是,扩展函数是静态分派的,即它们不是按接收器类型虚拟的。这意味着被调用的扩展函数由调用该函数的表达式的类型决定,而不是由在运行时计算该表达式的结果的类型决定。

简而言之,扩展函数不会做您对常规多态性所期望的事情。对于此变通方法,这意味着默认函数不能像常规函数一样被覆盖。如果你试图覆盖它,你会得到一些奇怪的行为,因为当你显式地处理子类时将调用“覆盖”版本,但是当你一般地处理接口时将调用扩展版本. 例如:

interface MyInterface {
    fun a()
}

fun MyInterface.b() {
    println("MyInterface.b() default implementation")
}

class MyInterfaceImpl : MyInterface {
    override fun a() {
        println("MyInterfaceImpl.a()")
    }

    fun b() {
        println("MyInterfaceImpl.b() \"overridden\" implementation")
    }
}

fun main(args: Array<String>) {
    val inst1: MyInterface = MyInterfaceImpl()
    inst1.a()
    inst1.b() // calls the "default" implementation

    val inst2: MyInterfaceImpl = MyInterfaceImpl() // could also just do "val inst2 = MyInterfaceImpl()" (the type is inferred)

    inst2.a()
    inst2.b() // calls the "overridden" implementation
}
Run Code Online (Sandbox Code Playgroud)


Tor*_*nik 9

从 Kotlin 1.4.0 开始,您可以使用以下编译器标志之一:

  • -Xjvm-default=all
  • -Xjvm-default=all-compatibility(为了与旧的 Kotlin 代码进行二进制兼容)

这将为所有接口启用 JVM 默认方法编译。

如果您想了解如何在 IDE 或 Maven/Gradle 项目中设置这些标志,请查看有关编译器选项的文档

问题KT-4779跟踪了这方面的进展,其中还包括对当前状态的有用总结。不应再使用注释和较旧的编译器标志@JvmDefault-Xjvm-default=enable-Xjvm-default=compatibility