Generics 有没有办法在Kotlin中从同一个泛型接口继承两次(使用不同的类型)?

Generics 有没有办法在Kotlin中从同一个泛型接口继承两次(使用不同的类型)?,generics,interface,multiple-inheritance,kotlin,Generics,Interface,Multiple Inheritance,Kotlin,我的代码中有一个场景,我希望一个类为两种不同的类型实现一个接口,如以下示例: interface Speaker<T> { fun talk(value: T) } class Multilinguist : Speaker<String>, Speaker<Float> { override fun talk(value: String) { println("greetings") } override

我的代码中有一个场景,我希望一个类为两种不同的类型实现一个接口,如以下示例:

interface Speaker<T> {
    fun talk(value: T)
}

class Multilinguist : Speaker<String>, Speaker<Float> {
    override fun talk(value: String) {
        println("greetings")
    }

    override fun talk(value: Float) {
        // Do something fun like transmit it along a serial port
    }
}
我知道一个可能的解决方案是实现以下代码,其中我使用
实现接口,然后自己检查类型并将它们委托给它们的函数

interface Speaker<T> {
    fun talk(value: T)
}

class Multilinguist : Speaker<Any> {
    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
    }
}
接口扬声器{
趣味演讲(价值:T)
}
类多语言:说话人{
覆盖趣味对话(值:任意){
何时(值){
是字符串->
内部谈话(价值)
是浮动->
内部谈话(价值)
} 
}
趣味internalTalk(值:字符串){
println(值)
}
有趣的内部对话(值:浮动){
//做一些有趣的事情,比如通过串口传输
}
}

然而,这感觉就像我正在删除关于类的用途的类型安全和通信,并且在自找麻烦。有没有更好的方法在Kotlin中实现这一点?此外,我在第一个示例中指出的不允许使用的原因是什么?接口不仅仅是我需要实现的签名契约,还是我在这里缺少了一些涉及泛型的东西?

是的,您缺少了JVM上泛型实现的一个重要细节:。简而言之,类的编译字节码实际上并不包含任何关于泛型类型的信息(除了一些关于类或方法是泛型的元数据)。所有类型检查都发生在编译时,在代码中没有保留泛型类型之后,只有
对象

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

interface Converter<T> {
    fun convert(t: T): T
}

class Reverser(): Converter<String> {
    override fun convert(t: String) = t.reversed()
}
以下是
反向器的字节码中的方法:

// 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;
    ...
要继承
转换器
接口,
反向器
应按顺序具有具有相同签名的方法,即类型擦除方法。如果实际的实现方法具有不同的签名,则添加一个。这里我们看到字节码中的第二个方法就是桥接方法(它调用第一个)

因此,多个通用接口实现可能会相互冲突,因为对于某个签名只能有一个桥接方法

此外,如果可能的话,Java和Kotlin都不会,而且参数有时也会有歧义,因此多重继承将非常有限

然而,随着时间的推移,情况会发生变化(具体化的泛型将在运行时保留实际类型),但我仍然不希望有多个泛型接口继承

// access flags 0x401
// signature (TT;)TT;
// declaration: T convert(T)
public abstract convert(Ljava/lang/Object;)Ljava/lang/Object;
// 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;
    ...