【问题标题】:Kotlin to Java interoperability in hierarchial interfaces分层接口中 Kotlin 到 Java 的互操作性
【发布时间】:2021-12-30 00:43:57
【问题描述】:

我有两个这样定义的模块:

interface Module {
    val parameter: JSONObject
}

abstract class FirstModule: Module {
    abstract val message: Message
}
....
// many more Modules

我为Module 创建了一个基本侦听器接口,为FirstModule 创建了另一个:

fun interface ModuleListener {
    fun ready(module: Module)
}

fun interface FirstModuleListener: ModuleListener {
    fun ready(module : FirstModule)

    override fun ready(module: Module) {
        ready(module as FirstModule)
    }
}

假设有一个名为 firstModule() 的函数,如下所示:

fun firstModule(listener: FirstModuleListener)

KOTLIN 中,当我调用firstModule() 时,事情按预期工作(让用户只用FirstModule 作为参数覆盖ready()

firstModule ( object: FirstModuleListener {
       override fun ready(module: FirstModule) {
           Log.v(TAG, "module: $module)
       }
    }
}

但在JAVA 中表现不同。即使其中一个已经被覆盖并且在子界面中有一些内容,它也期望用户覆盖这两个接口方法。

firstModule(new FirstModuleListener() {
   @Override public void ready(Module module) { }

   @Override public void ready(FirstModule module) {}
}

如何跳过 Java 中的第一个被覆盖的方法调用。是不是因为 Kotlin 中的方法定义支持而不是 Java 中的?

【问题讨论】:

  • 不是一个答案,但值得注意的是,在公共方法中这种不安全的演员 ready(module as FirstModule) 似乎是一场等待发生的灾难,如果用户知道这是一个 FirstModule 他们已经可以使用其他方法,所以这个方法除了保证崩溃之外没有其他用途。当然,由于缺乏上下文,或者这只是说明问题的示例,这可能看起来很奇怪。
  • 方法 firstModule 仅接受 FirstModuleListener 最终要求用户(在 kotlin 中)仅覆盖 ready(module: FirstModule)。所以我认为这没问题。但感谢您指出。

标签: kotlin kotlin-interop


【解决方案1】:

本质上:当kotlin实现FirstModuleListener interface时,它仍然实现了两个ready()

FirstModuleListener中,fun ready(module: FirstModule)是一个抽象方法,并且

override fun ready(module: Module) {
        ready(module as FirstModule)
    }

这是一个具体的实现方法。

kotlin中的接口可以定义具体的方法,其背后的kotlin编译器转换成Java中对应的静态方法。您的 FirstModuleListener 定义了抽象方法和非抽象方法。

Kotlin 会在编译时生成一个DefaultImpls 静态内部类来实现FirstModuleListenerready(module: Module) 方法。

public interface FirstModuleListener extends ModuleListener {
   void ready(@NotNull FirstModule var1);

   void ready(@NotNull Module var1);

   @Metadata(
      mv = {1, 5, 1},
      k = 3
   )
   public static final class DefaultImpls {
      public static void ready(@NotNull FirstModuleListener $this, @NotNull Module module) {
         Intrinsics.checkNotNullParameter(module, "module");
         $this.ready((FirstModule)module);
      }
   }
}

在实现FirstModuleListener 时,ready() 本质上调用ready()DefaultImpls

 firstModule((FirstModuleListener)(new FirstModuleListener() {
         public void ready(@NotNull FirstModule module) {
            Intrinsics.checkNotNullParameter(module, "module");
         }

         public void ready(@NotNull Module module) {
            Intrinsics.checkNotNullParameter(module, "module");
            FirstModuleListener.DefaultImpls.ready(this, module);
         }
      }));

这与 Java 没有什么不同。

############################################## ##

interface Module {
    val parameter: JSONObject
}

abstract class FirstModule : Module {
    abstract val message: String
}

abstract class SecondModule : Module {
    abstract val message: String
}

interface ModuleListener {
    fun ready(module: Module)
}

fun firstModule(listener: ModuleListener)

fun main() {
    firstModule(object : ModuleListener {
        override fun ready(module: Module) {
            when (module) {
                is FirstModule -> {
                    Log.v(TAG, "module: $module)
                }
                is SecondModule -> {
                    //...
                }
            }
        }
    })
}

【讨论】:

  • 确实和Java没有什么不同。但是话又说回来,有什么办法可以摆脱这种情况吗?我的要求是允许用户只能访问与ready 方法相关的特定模块。如果我使用抽象类而不是接口,则无法使用 lambda。
  • 在我看来,应该从传入的参数来判断类型,这样就只需要一个ready()即可。
  • 我重新编辑了答案,你看看对你有没有帮助。
猜你喜欢
  • 2015-03-27
  • 1970-01-01
  • 2020-06-21
  • 2018-06-18
  • 2017-07-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-18
相关资源
最近更新 更多