Jalson1982
Jalson1982

Reputation: 420

Kotlin Android reflection get class in runtime

I have a response from the server that returns me different objects and each object has a type. How I can match class in runtime with the type I get from the response? This code for example works but I am trying to get rid of the when statement and make it more dynamic.

sealed class Tile(
    private val id: String,
    private val type: String,
    private val title: String,
) {
    class RedTile(
        id: String,
        type: String,
        title: String,
        val stuff: String
    ) : Tile(id, type, title) {
        companion object {
            const val TYPE = "RedTile"
        }
    }
    class BlueTile(
        id: String,
        type: String,
        title: String,
        val value: Int
    ) : Tile(id, type, title) {
        companion object {
            const val TYPE = "BlueTile"
        }
    }

    companion object {
        fun parseArray(jsonArrayString: String): List<Tile> {
        return JsonParser.parseString(jsonArrayString).asJsonArray
            .map { tileJsonObject ->
                tileJsonObject as JsonObject
                val tile = when (tileJsonObject.get("type").asString) {
                    Red.TYPE -> Red::class.java
                    Blue.TYPE -> Blue::class.java
                    else -> Red::class.java
                }
                Gson().fromJson(tileJsonObject, tile)
            }
    }
    }
}

Upvotes: 0

Views: 891

Answers (1)

broot
broot

Reputation: 28462

Using a static when isn't really that bad, but I suggest moving it to a separate Tile.getTileClassByType() function for keeping the code clean. If you prefer to search for classes dynamically, then you can use the following:

companion object {
    private val tileClassesByType = Tile::class.sealedSubclasses
        .associateBy { it.simpleName!! }
    
    fun parseArray(jsonArrayString: String): List<Tile> {
        ...
        val tile = getTileClassByType(tileJsonObject.get("type").asString) ?: RedTile::class
        Gson().fromJson(tileJsonObject, tile.java)
        ...
    }

    fun getTileClassByType(type: String): KClass<out Tile>? = tileClassesByType[type]
}

Below is my original answer, before detailed information was provided in the question. It is discouraged as it is always better to avoid loading classes dynamically by name.


You can use Class.forName() from Java stdlib:

val type = tileJsonObject.get("type").asString
Class.forName("com.example.$type")

Just watch out for what you receive in this type field. If this data is untrusted then you should validate it before using, so the other side can't instantiate objects of arbitrary classes in your application.

Upvotes: 1

Related Questions