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

时间:2017-05-25 11:55:27

标签: kotlin

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

interface Foo {
    fun bar(): String {
        return "baz"
    }
}

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

4 个答案:

答案 0 :(得分:18)

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

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

interface Foo {
    @JvmDefault
    fun bar(): String {
        return "baz"
    }
}

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

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

答案 1 :(得分:12)

请参阅the related issue

评论中有一条建议:

  

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

答案 2 :(得分:0)

与早期版本的Java8不同,Kotlin可以在界面中使用默认实现。

Foo接口实现到Java类时。 Kotlin隐藏了那些接口方法的实现。如上所述here

  

数组在Java平台上与原始数据类型一起使用,以避免装箱/拆箱操作的成本。由于Kotlin隐藏了这些实现细节,因此需要一种解决方法来与Java代码进行交互

这是上面链接中的Arrays特有的,但它也适用于所有类(可能是为了支持早期版本的Java8)。

修改

以上解释基于意见。

我遇到的一件事就是主要原因。

  

Kotlin二进制文件使用java字节码版本1.8编译,接口中没有默认方法。他们面临着解决问题的关键问题。

答案 3 :(得分:0)

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

例如,您正在做的事情可以通过以下方式完成:

interface Foo {
    // presumably other stuff
}

fun Foo.bar(): String {
    return "baz"
}

有关它们的更多信息,请参见the docs on extension functions

一个值得注意的“陷阱”

  

我们要强调的是,扩展功能是静态分配的,即,它们不是由接收者类型虚拟的。这意味着被调用的扩展函数是由调用该函数的表达式的类型决定的,而不是由运行时对该表达式求值的结果的类型决定的。

简单地说,扩展功能不能满足常规多态性的预期。此替代方法的含义是,不能像常规功能那样覆盖默认功能。如果您尝试覆盖它,则会出现一些奇怪的行为,因为在您显式处理子类时将调用“覆盖”版本,而在一般情况下使用接口时将调用扩展版本。 。例如:

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
}