科特林。尝试使用具体化类型来解析列表和数组

lud*_*nus 4 kotlin kotlin-reified-type-parameters

我尝试在解析 json 时使用具体化类型。它可以完美地处理单个 json 条目,但无法处理列表。

问题:

  1. String.parseList() 方法中缺少什么?
  2. 尽管赋值提前通过了一行,但为什么在 .first() 上出现 ClassCastException?
    package qa

    import com.fasterxml.jackson.databind.ObjectMapper
    import org.slf4j.LoggerFactory
    import org.testng.Assert
    import org.testng.annotations.Test

    class ReifiedParseListTest {

        data class User(var name: String = "userName", var age: Int = 0)

        val log = LoggerFactory.getLogger(this.javaClass.name)
        val objectMapper = ObjectMapper()
        val json: String = """[{"name":"Alice","age":1},{"name":"Bob","age":2}]"""
        val expected: String = "[User(name=Alice, age=1), User(name=Bob, age=2)]"


        inline fun <reified V> String.parseList(): List<V> = objectMapper
                .readValue(this, Array<V>::class.java).toList()


        @Test
        fun checkParseList_OK() {
            val actual: List<User> = objectMapper
                    .readValue(json, Array<User>::class.java).toList()

            log.info("actual.first() is of type: {}", actual.first().javaClass)
            Assert.assertEquals(actual.toString(), expected)
        }

        @Test
        fun checkParseListReified_FAILS() {
            val actual: List<User> = json.parseList<User>()
            Assert.assertEquals(actual.toString(), expected)
            // java.lang.AssertionError:
            // Expected :[User(name=Alice, age=1), User(name=Bob, age=2)]
            // Actual   :[{name=Alice, age=1}, {name=Bob, age=2}]
        }

        @Test
        fun checkParseListReifiedClassCast_FAILS() {
            val actual: List<User> = json.parseList<User>()
            log.info("actual.first() is of type: {}", actual.first().javaClass)
            // java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to qa.ReifiedParseListTest$User
        }

    }
Run Code Online (Sandbox Code Playgroud)

Ale*_*hin 5

在这种情况下,reified有助于传播类型的类,但仍然存在类型擦除。
为了避免这种情况,你可以使用类似的东西JavaType

inline fun <reified V> String.parseList(): List<V> {
    return objectMapper.readValue(this, objectMapper.getTypeFactory()
        .constructCollectionType(List::class.java, V::class.java))
}
Run Code Online (Sandbox Code Playgroud)

请注意,如果没有,reified我们将无法使用V::class.java

现在回答你的第二个问题,为什么你会得到val actual-答案又是类型擦除,并对平台类型进行一些混淆。List<User>ClassCastException

如果你看看这个函数返回的内容(这是你没有asList()调用的函数:

inline fun <reified V> String.parseList() = 
        objectMapper.readValue(this, Array<V>::class.java)
Run Code Online (Sandbox Code Playgroud)

你会注意到它返回了Array<???>!,这是 Kotlin 表达“这是来自 Java 的东西,我希望它能工作,但我不能保证”的方式。现在,通过调用toList()它可以让编译器放松,说“是的,最终我们返回一个 Kotlin 类型,那就没问题了”。但这实际上是一个虚假的承诺。

你得到的内容Array<Any>充满了,当它们根据我们给编译器的错误承诺LinkedHashMap进行转换时,当然会失败。User