科特林反射

Kotlin reflection

使用 Retrofit,我正在获取数据并获得 JSONObject 结构如下:

{
    "token": "some-token",
    "tiles": [
        {
            "id": "id-1",
            "type": "RedTile",
            "title": "This red title",
            "stuff": "hello world"
        },
        {
            "id": "id-2",
            "type": "BlueTile",
            "title": "This blue title",
            "value": 200
        }
    ]
}

现在,当我收到此响应时,我需要制作一个解析器,它实际上会从每个图块中获取类型并将其与应用程序中已经存在的 class 相匹配。我基本上需要从这个响应中创建一个带有令牌和图块列表的新响应,但在这种情况下,图块将反映到应用程序中的实际 class。

知道什么是正确的做法吗?!

我不知道你的 类 长什么样,但假设是这样的:

sealed interface Tile
data class RedTile(val title: String, val stuff: String) : Tile
data class BlueTile(val title: String, val value: Int) : Tile

那么你可以将其作为响应对象

data class Response (
    val token: String,
    val tiles: List<TileResponse>
) {
    fun getTilesObjects() = tiles.mapNotNull { it.toTile() }
}

data class TileResponse (
    val id: String,
    val type: String,
    val title: String,
    val stuff: String? = null,
    val value: Int? = null
){
    fun toTile() : Tile? {
        if (type == "RedTile" && stuff != null) return RedTile(title, stuff)
        if (type == "BlueTile" && value != null) return BlueTile(title, value)
        return null
    }
}

然后您可以对单个 TileResponse 对象调用 toTile() 或通过对响应对象 getTilesObjects() 执行

来获取完整列表

因为您在问题标题中提到了 Reflection:

import kotlin.reflect.full.declaredMemberProperties

data class TileResponse(val id: String, val type: String, val title: String, val stuff: String? = null, val value: Int? = null)
data class Response(val token: String, val tiles: List<Tile>)

sealed interface Tile
data class RedTile(val title: String, val stuff: String) : Tile
data class BlueTile(val title: String, val value: Int) : Tile

fun getTiles(): List<Tile> {
  return tileResponses
    .filter { tileResponse -> mapTypeToClass.containsKey(tileResponse.type) }
    .map { tileResponse ->
      val clazz = mapTypeToClass[tileResponse.type]!!
      val constructor = clazz.constructors.first()
      val declaredMemberProperties = tileResponse::class.declaredMemberProperties
      val fields = constructor.parameters.map { parameter ->
        parameter to declaredMemberProperties.first { it.name == parameter.name }
      }
      val arguments = fields.associate { (parameter, property) ->
        parameter to property.call(tileResponse)
      }
      constructor.callBy(arguments)
    }
}

val tileResponses = listOf(
  TileResponse("id-1", "RedTile", "This red title", "hello world", null),
  TileResponse("id-2", "BlueTile", "This blue title", "null", 200)
)

val mapTypeToClass = mapOf(
  "RedTile" to RedTile::class,
  "BlueTile" to BlueTile::class,
)

val response = Response("some-token", getTiles())