在 Kotlin 中将映射值转换为数据 Class

Transform Map Values to Data Class in Kotlin

我有一个地图,其值是不同对象类型(LocalDate、LocalTime、Float、List(Float))的数组列表。

我想将地图值转换为我创建的自定义数据class。

我已经尝试解构映射值组件,但是我收到错误消息“Map.Entry 类型的解构声明初始值设定项必须具有 'component3()' 函数”。

我将如何执行下面的转换?

Blockquote

    val localDate1 = LocalDate.now()
    val localTime1 = LocalTime.now()
    val float1 = 1f
    val floatList1 = listOf<Float>(1f, 2f, 3f)

    val localDate2 = LocalDate.now()
    val localTime2 = LocalTime.now()
    val float2 = 2f
    val floatList2 = listOf<Float>(4f, 5f, 6f)

    val myMap = HashMap<String, Any>()
    myMap["keyOne"] = arrayListOf<Any>(localDate1, localTime1, float1, floatList1)
    myMap["keyTwo"] = arrayListOf<Any>(localDate2, localTime2, float2, floatList2)

    val newMap = myMap.mapValues { (date, time, float, floatList) -> /*Here i am getting destructuring error*/
        CustomObject(
            date,
            time,
            float,
            floatList
        )
    }

Blockquote

data class CustomObject (
    val date: LocalDate,
    val time: LocalTime,
    val float: Float,
    val floatList: List<Float>
)

Map.mapValues() takes a lambda whose receiver is the map Entry,不是它的值。 (该方法是以 lambda 的结果命名的,而不是它的参数。) 所以你不能那样解构它。

还有类型转换问题:myMap 是一个值为 Any 的映射,但您似乎假设每个值实际上是 ListAny ](实际元素是按适当顺序排列的适当类型)。所以你需要做必要的转换来告诉编译器它们是什么类型(或者至少,你假设它们是什么类型......)

因此您可以将其解构为一个单独的步骤,例如:

val newMap = myMap.mapValues { entry ->
    val (date, time, float, floatList) = entry.value as List<Any>

    CustomObject(
        date as LocalDate,
        time as LocalTime,
        float as Float,
        floatList as List<Float>
    )
}

或者您可以直接使用数组值:

val newMap = (myMap as Map<String, List<Any>>).mapValues {
    CustomObject(
            it.value[0] as LocalDate,
            it.value[1] as LocalTime,
            it.value[2] as Float,
            it.value[3] as List<Float>
    )
}

但是我认为首先避免列出列表会好得多。您需要大量额外的代码来创建列表然后转换它们——但其中大部分代码都是不安全的:如果列表不够长,每个解构都可能会失败并出现异常(可能是 ArrayIndexOutOfBoundsException),并且每个转换都可能因 ClassCastException 而失败。转换总是有代码味道,尤其是像这样未经检查的; Kotlin 的类型系统足够强大,可以表达大多数你可能想要的东西,所以像这样绕过它往往表明设计可以改进。

因此,如果有任何方法可以直接创建自定义对象,那可能会更短、更安全、更快且更易于阅读。

import java.time.LocalDate
import java.time.LocalTime

data class CustomObject(
  val date: LocalDate,
  val time: LocalTime,
  val float: Float,
  val floatList: List<Float>
)

val myMap = mapOf(
  "keyOne" to listOf(LocalDate.now(), LocalTime.now(), 1f, listOf(1f, 2f, 3f)),
  "keyTwo" to listOf(LocalDate.now(), LocalTime.now(), 2f, listOf(4f, 5f, 6f))
)

val newMap = myMap.mapValues {
  CustomObject(
    it.value[0] as LocalDate,
    it.value[1] as LocalTime,
    it.value[2] as Float,
    (it.value[3] as List<*>).map { fl -> fl as Float }
  )
}

println(newMap)