任何方式在Kotlin中从相同的通用接口inheritance两次(使用单独的types)?

我在我的代码中有一个场景,我想要一个类实现两个不同types的接口,如下面的示例:

interface Speaker { fun talk(value: T) } class Multilinguist : Speaker, Speaker { override fun talk(value: String) { println("greetings") } override fun talk(value: Float) { // Do something fun like transmit it along a serial port } } 

科特林对此并不满意,理由是:

 Type parameter T of 'Speaker' has inconsistent values: kotlin.String, kotlin.Float A supertype appears twice 

我知道一个可能的解决方案是实现下面的代码,在那里我用实现接口,然后检查自己的types并将它们委托给它们的函数。

 interface Speaker { fun talk(value: T) } class Multilinguist : Speaker { override fun talk(value: Any) { when (value) { is String -> internalTalk(value) is Float -> internalTalk(value) } } fun internalTalk(value: String) { println(value) } fun internalTalk(value: Float) { // Do something fun like transmit it along a serial port } } 

然而,这感觉就像我正在去除types安全和沟通关于什么类使用,并要求麻烦下线。 在Kotlin中有更好的实现方法吗? 另外 – 它背后的原因是什么,不被允许像我在第一个样本中指出的方式? 接口不仅仅是我需要执行的签名合同,还是我在这里涉及generics的东西?

是的,您错过了JVM上generics实现的一个重要细节: types删除 。 简而言之,类的编译字节码实际上并不包含关于genericstypes的任何信息(除了关于类或方法通用的一些元数据外)。 所有的types检查都是在编译时进行的,之后代码中没有genericstypes,只有Object

要发现你的问题,只要看看字节码(在IDEA中, Tools -> Kotlin -> Show Kotlin Bytecode ,或任何其他工具)。 我们来考虑这个简单的例子:

 interface Converter { fun convert(t: T): T } class Reverser(): Converter { override fun convert(t: String) = t.reversed() } 

Converter的字节码中,通用types被擦除:

 // access flags 0x401 // signature (TT;)TT; // declaration: T convert(T) public abstract convert(Ljava/lang/Object;)Ljava/lang/Object; 

以下是在Reverser字节码中find的方法:

 // access flags 0x1 public convert(Ljava/lang/String;)Ljava/lang/String; ... // access flags 0x1041 public synthetic bridge convert(Ljava/lang/Object;)Ljava/lang/Object; ... INVOKEVIRTUAL Reverser.convert (Ljava/lang/String;)Ljava/lang/String; ... 

为了inheritanceConverter接口, Reverser应该有一个相同签名的方法,也就是一个types被擦除。 如果实际的实现方法有不同的签名,则添加一个桥接方法 。 在这里我们看到字节码中的第二个方法正是桥接方法(并且它调用了第一个方法)。

所以,多个通用接口实现会相互冲突,因为对于特定的签名,只能有一个桥接方法。

而且,如果可能的话,Java和Kotlin 都没有基于返回值types的方法重载 ,有时候在参数中也会有歧义,所以多重inheritance是相当有限的。

事情,但是,将改变与项目Valhalla (被泛化的generics将在运行时保留实际types),但我仍然不希望多个通用接口inheritance。