【问题标题】:How does Kotlin choose the generic overloaded function to call?Kotlin 如何选择泛型重载函数来调用?
【发布时间】:2021-02-17 12:49:37
【问题描述】:

我正在尝试编写序列化函数,以便能够序列化 Kotlin 中的任何向量 (=ArrayList),以及扩展具有 toBinary() 函数的 Serialize 类的原始类型和类。 我还有一个自定义的 WriteDataStream 类(下面的代码)来序列化具有正确格式、字节序等的字段。

我是 Kotlin 的新手,但有 C++ 方面的经验。在 C++ 中,我使用模板和模板专业化来轻松解决这个问题,但在使用 Kotlin 时,我已经苦苦挣扎了几天,但没有成功。

我有一个自定义矢量类MyVector,它扩展了ArrayList 并增加了一个最大尺寸。我想用任何泛型类型T 序列化它,包括像MyVector<MyVector<MyClass>> 这样的内部向量。

我的WriteDataStream 包含以下内容:

inline fun <reified T> write(vector: MyVector<T>) {
    this.writeSize(vector.size.toULong(), vector.MAX_SIZE)
    for (element in vector) {
        write<T>(element)
    }
}

inline fun <reified T: Serialize> write(value: T) {
    writeSerialize(value as Serialize)
}

inline fun <reified T> write(value: T) {
    when (T::class) {
        UByte::class -> {
            writeUInt8(value as UByte)
        }
        UShort::class -> {
            writeUInt16(value as UShort)
        }
        UInt::class -> {
            writeUInt32(value as UInt)
        }
        ULong::class -> {
            writeUInt64(value as ULong)
        }
        Byte::class -> {
            writeInt8(value as Byte)
        }
        Short::class -> {
            writeInt16(value as Short)
        }
        Int::class -> {
            writeInt32(value as Int)
        }
        Long::class -> {
            writeInt64(value as Long)
        }
        Boolean::class -> {
            writeBoolean(value as Boolean)
        }
        Float::class -> {
            writeFloat(value as Float)
        }
        Double::class -> {
            writeDouble(value as Double)
        }
        else -> {
            error("Default serialization:" + T::class.qualifiedName)
        }
    }
}

所有底层函数 writeXXX() 都经过测试并且工作正常。但是,当使用扩展 Serialize 的类来序列化 MyVector 时,我属于“默认序列化”的情况:

@Test
fun writeVectorOfStructure() {
    class TestStructure: Serialize() {
        override fun toBinary(stream: WriteDataStream) {
            stream.writeUInt32(17U)
            stream.writeUInt8(3U)
            stream.writeDouble(555.555)
        }
    }
    val value = MyVector<TestStructure>(MAX_SIZE, arrayListOf(TestStructure(), TestStructure()))
    writeStream.write(value)
    val bytes: UByteArray = writeStream.byteArray()
    Assert.assertEquals(bytes.size, 28) // = 2 (for size) + 2*(4+1+8) = 28 bytes
}

所以我的问题是:为什么 Kotlin 不使用该功能

inline fun <reified T: Serialize> write(value: T)
        

当它使用通用的T = Serialize 序列化向量的元素 (write&lt;T&gt;(element)),而是使用更通用的元素时?

inline fun <reified T> write(value: T)

在 C++ 中,编译器总是使用最适合的函数。

有没有办法克服 Kotlin 中的这个限制? 我尝试过使用和不使用具体类型,我也尝试过非泛型函数:inline fun write(value: Serialize),但没有成功。唯一可行的方法是在完全通用的inline fun &lt;reified T&gt; write(value: T) 中为“Serialize”类的“实例”添加一个案例,但这并不是一个很好的解决方案。

谢谢!

【问题讨论】:

    标签: kotlin serialization


    【解决方案1】:

    JVM and its bad implementation of generics

    您是 Java 泛型实现的受害者,更具体地说是erasureC++ 使用所谓的类型扩展来实现泛型,这意味着如果您在 runtime 声明 MyType&lt;A&gt;MyType&lt;B&gt;,您将拥有两种不同的类型,语言 runtime 将为您创建它们。

    另一方面,Java 所做的称为擦除实现。所以在java世界中当你说List&lt;String&gt;List&lt;Integer&gt;时,在运行时它们都是相同的类型,也就是说系统没有任何信息来区分这两者,它们是List类型(请注意,没有类型参数,它在编译过程中被删除了)。

    让我们反编译您的代码并亲自查看

    我在 kotlin 中编写了以下代码,它与您的匹配

    class SomeType {
        inline fun <reified T: String> write(value: T) {}
        inline fun <reified T> write(value: T) {}
    
        inline fun <reified T: Any> write(vector: List<T>) {
            for (element in vector) {
                write(element)
            }
        }
    }
    

    当我反编译代码时,它会给我以下信息。 (仅包含相关代码)

    public final class SomeType {
       public final void write(@NotNull String value) {}
       public final void write(Object value) {}
    
       public final void write(@NotNull List vector) {
          boolean var6;
          for(Iterator var4 = vector.iterator(); var4.hasNext(); var6 = false) {
             Object element = var4.next();
          }
       }
    }
    

    查看write(vector: List&lt;T&gt;) 方法的反编译。参数类型更改为List,即Raw Type,其组件是对象。

    对于Object,最佳方法匹配是public final void write(Object value),而不是StringSerialize

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-03-30
      • 1970-01-01
      • 1970-01-01
      • 2010-09-27
      • 1970-01-01
      • 2012-04-12
      • 1970-01-01
      • 2021-06-14
      相关资源
      最近更新 更多