Java与Kotlin混合编程之@JvmDefault

在这里插入图片描述
虽然kotlin与Java很好地进行了兼容,但使用不当仍然会遇到不少问题。

1. 问题:无法代理default方法


我们定一个包含default方法的Java接口及其实现类

//Base.java
public interface Base {
    
    
    default void print() {
    
    
        System.out.println("Base");
    }
}

//BaseImpl.java
class BaseImpl implements Base {
    
    
    @Override
    public void print() {
    
    
        System.out.println("BaseImpl");
    }
}

此接口如果用做koltlin的类代理中:

//Derived.kt

class Derived(b: Base) : Base by b

fun main() {
    
    
    val b = BaseImpl()
    Derived(b).print()
}

输出结果:
在这里插入图片描述

虽然代理对象是BaseImpl,但其print()方法并没有被重写,依然是执行的default的方法。


2. 原因分析


by关键字本质上是一个语法糖,帮你省略代理模式所需的模板代码,但是java接口的default方法不会被代理
在这里插入图片描述
kt反编译后的java代码可以看到,并没有实现print()方法


3. Kotlin没有default方法


使用kotlin定义接口Base,其他保持不变

//Base.kt
interface Base {
    
    
    fun print() {
    
    
        print("Base")
    }
}

此时反编译
在这里插入图片描述
可以返现,print()方法被正常代理了

Kotlin接口虽然可以定义方法体实现,但是实现机制与java的default方法实现机制不同,Kotlin中也没有default关键字
在这里插入图片描述
Base通过DefaultImpls静态类添加默认实现,其子类可以通过super调用实现

Kotlin子类

Kotlin中定义BaseImpl

class BaseImpl : Base {
    
    
    override fun print() {
    
    
        super.print()
        print("BaseImpl")
    }
}

在这里插入图片描述

Java子类

Kotlin接口中定义的方法体并非真正给你的default方法,所以当Java子类继承Kotlin接口时,仍然要重写print(),否则会报错
在这里插入图片描述
Kotlin接口的方法可以添加@JvmDefault注解,实现java中default关键字的效果:

https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-jvm-default/

在这里插入图片描述

其中,最后一句话也很明确的说明了无法对default方法进行代理


4. 结论


回到本文最初的问题,我们因该怎么避免呢?

首先,应该尽量避免Kotlin和Java的混编,特别是避免在继承上产生关系,让上帝的归上帝,凯撒的归凯撒
在这里插入图片描述

若迫不得已需要像本例一样(在Kotlin中实现一个Java接口),当有default方法时,需要重写default方法,并手动调用代理,不要寄希望于by关键字

猜你喜欢

转载自blog.csdn.net/vitaviva/article/details/108292281