科特林反射
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())
使用 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())