我可以覆盖 returns 单位类型 Java 的 kotlin 方法吗

Can I override kotlin method which returns Unit type in Java

主要问题:如何在java returns 单位类型中覆盖 kotlin 方法?

我正在尝试在 java 中使用 kotlin 库,并且有一个名称为 override fun invoke(): Unit 的方法我必须实现。

但是,java 编译器一直告诉我 return type void is not compatible with Unit

我在 java 中尝试了 public Unit invoke() { return Unit.INSTANCE; } 但出现了编译错误。

invoke()' in 'myproject' clashes with 'invoke()' in 'library'; 
attempting to use incompatible return type

kotlin 接口(在库中)

interface MessageHandler<M : Message> : (Message) -> Unit {

  override fun invoke(message: Message): Unit =
    if (messageType.isAssignableFrom(message.javaClass)) {
      println("invoked ${message.javaClass.simpleName}")
    } else {
      throw IllegalArgumentException("Unsupported message type ${message.javaClass.simpleName}")
    }

}

java摘要class(在我的项目中)

public abstract class AbstractMessageHandler<T extends Message> implements MessageHandler<T> {

    @Override
    public void invoke(@NotNull Message message) {
        //this is where the compile error occurs.
    }
}

错误信息

(Message) in AbstractMessageHandler cannot implement invoke(P1) in Function1
    public void invoke(@NotNull Message message) {
                ^
  return type void is not compatible with Unit
  where P1,R are type-variables:
    P1 extends Object declared in interface Function1
    R extends Object declared in interface Function1

看起来像是一个 4 年前未解决的错误:https://youtrack.jetbrains.com/issue/KT-15964

引用:

I don't think this is possible now because of compatibility with older binaries. We'll see what we can do, thank you for the report.

An ugly workaround would be to use the nullable type Unit?:

interface TestA<out T> {
    fun foo(): T
}

abstract class TestB : TestA<Unit?> {
    override fun foo(): Unit? {
        // ...
        return Unit
    }
}

With this, you won't be required to override foo in a Java subclass. If you want to override it though, the signature in the Java subclass should be public Unit foo().

如果您不控制图书馆,我不确定您能做些什么。

  1. 如果您控制 Kotlin 库,您可以:
  • 继承自java.util.function.Consumer:

    interface MessageHandler<M : Message> : Consumer<Message> {
    
         override fun accept(message: Message): Unit =
             if (messageType.isAssignableFrom(message.javaClass)) {
                 println("invoked ${message.javaClass.simpleName}")
             } else {
                 throw IllegalArgumentException("Unsupported message type ${message.javaClass.simpleName}")
             }
    
    }
    
  • 或者在您的 Java 代码中使用 Any 而不是 Unit 和 return null

      interface MessageHandler<M : Message> : (Message) -> Any {
    
      override fun accept(message: Message): Any =
          if (messageType.isAssignableFrom(message.javaClass)) {
              println("invoked ${message.javaClass.simpleName}")
          } else {
              throw IllegalArgumentException("Unsupported message type ${message.javaClass.simpleName}")
          }
    
    }
    
  1. 如果不能,我认为最好在 Kotlin 中编写 AbstractMessageHandler 及其所有子 类:

    abstract class AbstractMessageHandler : MessageHandler<Message> {
       override fun invoke(message: Message) {
        // ...
       }
    }
    
  • 但是如果你不调用默认值 MessageHandler.invoke 并且你的代码不会与 MessageHandler 类型相关,你可以简单地继承自 java.util.function.Consumer :

      public  class AbstractMessageHandler implements Consumer<Message> {
         @Override
         public void accept(Message message) {
            // ...
         }
      }
    

在错误之前提一下:

(Message) in AbstractMessageHandler cannot implement invoke(P1) in Function1
...

您应该看到如下内容:

'invoke(Message)' in '...MessageHandler' clashes with 'invoke(P1)' in 
'kotlin.jvm.functions.Function1'; attempting to use incompatible return type

因为 Java 编译器看到两个接口,一个来自 (Message) -> Unit:

public interface kotlin.jvm.functions.Function1 {
   Object invoke(Messsage var1);
}

另一个来自MessageHandler

public interface MessageHandler extends Function1 {
   void invoke(@NotNull Message var1);
}

// (Some details were ommitted for simplicity)

这在 Java 中是不可能的,因为 MessageHandler.invoke 尝试用不同的 return 类型覆盖 Function1.invoke,如 jls-8.4.8.3 中所述,

If a method declaration d1 with return type R1 overrides or hides the declaration of another method d2 with return type R2, then d1 must be return-type-substitutable (§8.4.5) for d2, or a compile-time error occurs.