0

So, I've been banging around in Kotlin reflection, and I can't seem to do the following:

object ThreadState {

    sealed class State {
object DANCE : State() {val validEvents: List<ThreadEvent> = listOf(ThreadEvent.Weave, ThreadEvent.Bob)}
object DUCK : State() {val validEvents: List<ThreadEvent> = listOf(ThreadEvent.Weave, ThreadEvent.Bob)}
object DODGE : State() {val validEvents: List<ThreadEvent> = listOf(ThreadEvent.Weave, ThreadEvent.Bob)}
.... Code
    }

.... Code
}

What I want to do is something like this:

val map = ThreadState.State::class.sealedSubclasses.map{ it to it.simpleName}.toMap()

map[ThreadState.State.DODGE]

And have it spit out the simple name of the class (in this case, DODGE).

The reason is because I can then simply reverse the map and get a value out from a name. I.e. ThreadState.fromString("DODGE") // ThreadState.State.DODGE

However, when I use ::sealedSubclasses, it gives a list of <KClass <out ThreadState.State>>

This is no bueno, because I cannot for the life of me find a way to get the ThreadState.State back out of any given element in the list of KClasses. I tried to run it as ThreadState.State but it told me that KClass can't be cast to ThreadState.State.

Any help would be massively appreciated.

In short - have a list of sealed classes that are singleton objects holding values. Need to make a map of the classes that conforms to <ThreadState, String>, where ThreadState is an instance of ThreadState.State and the string is just the simple name of the class.

I am really open to solutions - certainly doesn't have to be reflection, generics also welcome.

6
  • how should State ever be an instance of ThreadState? Commented Dec 14, 2018 at 8:51
  • I'm not quite sure I understand the question. ThreadState.State.Dance, for example, will allow access to the data for that class and also function as an enum. Commented Dec 14, 2018 at 8:52
  • ThreadState is an object. State is an inner class of ThreadState... otherwise there isn't any relation between the two... So you probably rather want a map of either String to ThreadState.State or viceversa... Commented Dec 14, 2018 at 8:54
  • do you want to get the actual objects? like State.DODGE, State.DUCK or do you want to get the classes of the objects? sealedSubclasses gives you the classes of it, not the objects... Commented Dec 14, 2018 at 9:02
  • @Roland I feel silly. I want to get the actual objects that inherit the State class. Commented Dec 14, 2018 at 9:07

2 Answers 2

2

Here you go:

val stateMap = ThreadState.State::class.sealedSubclasses.asSequence()
  .map { it.simpleName to it.objectInstance } // note the objectInstance call
  .toMap()

Also note the documentation of objectInstance:

The instance of the object declaration, or null if this class is not an object declaration.

For testing purposes I added the following function to your State-class:

fun printMe() = println("class: ${this::class.simpleName} object: $this")

Now you can access the objects as follows:

stateMap["DODGE"]?.printMe()
// will then print the same as:
ThreadState.State.DODGE.printMe()

I question that it is worth to go that route, but you know the use-case, I don't... maybe also something like delegated properties could be useful to you?

However: If you want to access validEvents you need to ensure that it is also a part of the State-class itself, i.e. you may need to add something like the following:

sealed class State {
  open val validEvents : List<ThreadEvent> = emptyList()

  // and then of course the subclasses need to override it:
  object DANCE : State() {override val validEvents: List<ThreadEvent> = listOf(ThreadEvent.Weave, ThreadEvent.Bob)} 

Only then will you be able to call also something like:

stateMap["DODGE"]?.validEvents
Sign up to request clarification or add additional context in comments.

2 Comments

WHOAAAAAA THIS IS PERFECT. How did you know to make it an object instance?
to be honest: I guessed... If you look up objectInstance however, it becomes clear: The instance of the object declaration, or null if this class is not an object declaration.
1

how about map[ThreadState.State.DODGE::class]? Or is it not applicable for you?

3 Comments

No, it totally works. When I do that and reverse the map, even if I return the object I can't access it's fields anymore. i.e. ThreadState.fromString("DODGE").validEvents ValidEvents doesn't exist anymore.
@JapanRob I probably don't clearly understand what's the problem here. What does fromString method does? Is it only when statement returning instances? And what does it mean that validEvents doesn't exist anymore?
That's okay, I got the answer. Thank you though! And I'm upvoting your response because the syntax is correct.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.