9

Im trying to get enum using its value but it throws an error saying No enum constant. The eg code is in Kotlin but it is same in Java Thanks in advance.

import java.util.*

enum class TaskAction constructor(val value: String) {
    ARCHIVE("Archive"), UN_FLAG("Un flag"), FLAG("Flag"), REDO("Redo"),
    READY("Ready"), EDIT("Edit"), DND("dnd"), DELETE("Delete"),
    IN_PROGRESS("In progress"), DONE("Done");
}


fun main(args: Array<String>) {
    showSomeThing(TaskAction.UN_FLAG.value)
}

fun showSomeThing(enum: String) {
    println(TaskAction.valueOf(enum))
}

Log:

Exception in thread "main" java.lang.IllegalArgumentException: No enum constant TaskAction.Un flag

Sai
  • 15,188
  • 20
  • 81
  • 121
  • "The eg code is in Kotlin but it is same in Java" - *what* is the same in Java? Given how many more people write Java than Kotlin, I'd strongly encourage you to write a Java question rather than a Kotlin one... – Jon Skeet Jan 04 '17 at 12:15
  • Maybe leave out `constructor`. Check this for comparison: https://kotlinlang.org/docs/reference/enum-classes.html – Roel Strolenberg Jan 04 '17 at 12:31
  • http://stackoverflow.com/a/1080914/2392960 – Januson Jan 04 '17 at 12:34
  • 1
    For tasks like this one — where you want to look up your enums by some attribute rather than their declared names — there are two standard solutions: (1) provide an automatic translation from value to name (like uppercase and replace space with underscore) (2) use a static `Map`. – Ole V.V. Jan 04 '17 at 12:49

2 Answers2

20

The valueOf method takes a string that should correspond the name of the enum constant. However, you extract the value of the enum and try to map that back to a constant. This does not work unless the value is equal to the name of the constant.

So your problem is that TaskAction.UN_FLAG.value returns "Un flag". Then you call TaskAction.valueOf("Un flag"), which cannot find any enum constant with the string you provided (because the name of the enum constant is UN_FLAG).

So basically: "Un flag" != "UN_FLAG"

Edit:

To fix the issue, I would create a function that finds the correct enum based on the value. I would place the function within the companion object of TaskAction. Like this:

companion object {
  fun from(s: String): TaskAction? = values().find { it.value == s }
}
marstran
  • 26,413
  • 5
  • 61
  • 67
6

I changed my enum class to something like below, I added a static Map. Because there is no way it maps the value and name by itself. I thought this may help some.

    enum class TaskAction constructor(val value: String) {
    ARCHIVE("Archive"), UN_FLAG("Un flag"), FLAG("Flag"), REDO("Redo"),
    READY("Ready"), EDIT("Edit"), DND("dnd"), DELETE("Delete"),
    IN_PROGRESS("In progress"), DONE("Done");

    companion object {
        private val lookup = HashMap<String, TaskAction>()

        init {
            TaskAction.values().map { lookup.put(it.value, it) }
        }

        fun getFromValue(value: String):TaskAction {
            return lookup[value]!!
        }
    }
}

Update:

Thank to @mfulton26 i simplified my code:

enum class TaskAction constructor(val value: String) {
    ARCHIVE("Archive"), UN_FLAG("Un flag"), FLAG("Flag"), REDO("Redo"),
    READY("Ready"), EDIT("Edit"), DND("dnd"), DELETE("Delete"),
    IN_PROGRESS("In progress"), DONE("Done");

    companion object {
        private val lookup = values().associateBy(TaskAction::value)
        fun fromValue(value: String):TaskAction = requireNotNull(lookup[value]) { "No TaskAction with value $value" }
    }
}

I even reduce the code to with the help of @marstran:

companion object {
    fun from(search: String): TaskAction =  requireNotNull(values().find { it.value == search }) { "No TaskAction with value $search" }
}
Sai
  • 15,188
  • 20
  • 81
  • 121
  • 2
    1. `fromValue` might be a better name than `getFromValue`. 2. `lookup` can be initialized simply as `private val lookup = values().associateBy(TaskAction::value)`. 3. You might want to throw an `IllegalArgumentException` instead of a `KotlinNullPointerException`. e.g. `return requireNotNull(lookup[value]) { "No TaskAction with value $value" }`. – mfulton26 Jan 04 '17 at 14:20