80

I have this enum:

enum class Types(val value: Int) {
    FOO(1)
    BAR(2)
    FOO_BAR(3)
}

How do I create an instance of that enum using an Int?

I tried doing something like this:

val type = Types.valueOf(1)

And I get the error:

Integer literal does not conform to the expected type String

Mahozad
  • 18,032
  • 13
  • 118
  • 133
pableiros
  • 14,932
  • 12
  • 99
  • 105

11 Answers11

100
enum class Types(val value: Int) {
    FOO(1),
    BAR(2),
    FOO_BAR(3);

    companion object {
        fun fromInt(value: Int) = Types.values().first { it.value == value }
    }
}

You may want to add a safety check for the range and return null.

Zoe
  • 27,060
  • 21
  • 118
  • 148
Francesc
  • 25,014
  • 10
  • 66
  • 84
  • Was value for an enum deprecated? This worked for me but I had to change it.value to it.ordinal – AlanKley Jan 30 '20 at 20:27
  • 3
    @AlanKley `it.value` is supposed to refer to the name defined in the constructor. In this case, it's `val value: Int`. Ordinal is something entirely different; it's the placement of the enum in the list. See [this](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-enum/ordinal.html). `it.ordinal` is NOT the same as referencing a custom-defined value, which is what's done here. If you put in 2 for an instance, you get FOO_BAR, because it's at values[2]. FOO_BAR's `ordinal` is 2, but the `value` is 3. – Zoe Mar 21 '20 at 15:57
  • `fun fromInt(value: Int) = values().firstOrNull { it.value == value } ?: throw IllegalArgumentException("Invalid type: $value")` – Kartik Apr 20 '23 at 01:22
60

Enum#valueOf is based on name. Which means in order to use that, you'd need to use valueof("FOO"). The valueof method consequently takes a String, which explains the error. A String isn't an Int, and types matter. The reason I mentioned what it does too, is so you know this isn't the method you're looking for.

If you want to grab one based on an int value, you need to define your own function to do so. You can get the values in an enum using values(), which returns an Array<Types> in this case. You can use firstOrNull as a safe approach, or first if you prefer an exception over null.

So add a companion object (which are static relative to the enum, so you can call Types.getByValue(1234) (Types.COMPANION.getByValue(1234) from Java) over Types.FOO.getByValue(1234).

companion object {
    private val VALUES = values()
    fun getByValue(value: Int) = VALUES.firstOrNull { it.value == value }
}

values() returns a new Array every time it's called, which means you should cache it locally to avoid re-creating one every single time you call getByValue. If you call values() when the method is called, you risk re-creating it repeatedly (depending on how many times you actually call it though), which is a waste of memory.

Admittedly, and as discussed in the comments, this may be an insignificant optimization, depending on your use. This means you can also do:

companion object {
    fun getByValue(value: Int) = values().firstOrNull { it.value == value }
}

if that's something you'd prefer for readability or some other reason.

The function could also be expanded and check based on multiple parameters, if that's something you want to do. These types of functions aren't limited to one argument.

Zoe
  • 27,060
  • 21
  • 118
  • 148
  • 2
    In kotlin everything is intented to be immutable, so everything is a copy of a copy of a copy. There are instructions like "let", "filter", "map" everywhere in your code and each one of them is leaking unused objects behind. Caching enum values sounds like micro-optimisation to me. – Anthony Chatellier Oct 22 '20 at 11:42
  • 1
    @AnthonyChatellier That is technically incorrect, and that bases itself in how Java works. Might not be true for Kotlin Native, but Native wasn't in the scope. See https://stackoverflow.com/a/32799324/6296561 - caching may not have much of an impact depending on how often the method is used, but if it's used frequently enough, it has a fairly decent impact. You're free to not include caching though – Zoe Oct 22 '20 at 11:58
  • 1
    I don't know what was technically incorrect but I agree with you, caching may not have much of an impact depending on how often the method is used. For example, on my laptop calling getByValue 6 000 000 000 times in 20 seconds did not even consume 1% of CPU for garbage collecting. So regarding this result, even if it is one line of code, I don't really know if it is worth it, it sound like micro-optmisation to me. – Anthony Chatellier Oct 23 '20 at 12:23
  • Since we care enough to avoid recreating the array , shouldn't we avoid firstOrNull too given the fact that it performs a linear search? Since we are anyway encapsulating the whole thing into a function writing an if-statement to check if given `Int` is in bounds ain't that hard. `fun getByValue(value: Int) = if(value >= 0 && value < VALUES.size) VALUES[value] else null` – fvalasiad Aug 08 '21 at 03:33
  • 2
    @fvalasiad No, because you're confusing values with ordinals. if you add a `BAR_BAZ(4738653)` and do a lookup for `VALUES[4738653]`, that'll throw because there's only four items in `VALUES`. Your solution would be `fun getByIndex(index: Int) = VALUES.getOrNull(index)`, not by value. It's fine if you want to get by index, but not if you want to get by value (which is what the question asked about) – Zoe Aug 08 '21 at 11:45
  • 1
    ... well, it'll return null thanks to the size check, but it won't return an item either way – Zoe Aug 08 '21 at 12:00
  • "If you want to grab one based on an int value, you need to define your own function to do so." - You would think that with this being a commonly used feature, JetBrains would have added it to the language by now. – Johann Feb 06 '22 at 11:31
31

If you are using integer value only to maintain order, which you need to access correct value, then you don't need any extra code. You can use build in value ordinal. Ordinal represents position of value in enum declaration.

Here is an example:

enum class Types {
    FOO,               //Types.FOO.ordinal == 0 also position == 0
    BAR,               //Types.BAR.ordinal == 1 also position == 1
    FOO_BAR            //Types.FOO_BAR.ordinal == 2 also position == 2
}

You can access ordinal value simply calling:

Types.FOO.ordinal

To get correct value of enum you can simply call:

Types.values()[0]      //Returns FOO
Types.values()[1]      //Returns BAR
Types.values()[2]      //Returns FOO_BAR

Types.values() returns enum values in order accordingly to declaration.

Summary:

Types.values(Types.FOO.ordinal) == Types.FOO //This is true

If integer values don't match order (int_value != enum.ordinal) or you are using different type (string, float...), than you need to iterate and compare your custom values as it was already mentioned in this thread.

  • 3
    Even if you're right, I'd warn against doing it this way. A developer might change the order of the items or add a new one in between, and create a bug that might be hard to notice. What a developer will never do is change the int values in the `enum` as suggested in the other answers, so that bug will never happen. – Igor Rodriguez Jul 14 '21 at 09:40
13

It really depends on what you actually want to do.

  • If you need a specific hardcoded enum value, then you can directly use Types.FOO
  • If you are receiving the value dynamically from somewhere else in your code, you should try to use the enum type directly in order not to have to perform this kind of conversions
  • If you are receiving the value from a webservice, there should be something in your deserialization tool to allow this kind of conversion (like Jackson's @JsonValue)
  • If you want to get the enum value based on one of its properties (like the value property here), then I'm afraid you'll have to implement your own conversion method, as @Zoe pointed out.

One way to implement this custom conversion is by adding a companion object with the conversion method:

enum class Types(val value: Int) {
    FOO(1),
    BAR(2),
    FOO_BAR(3);

    companion object {
        private val types = values().associate { it.value to it }

        fun findByValue(value: Int): Types? = types[value]
    }
}

Companion objects in Kotlin are meant to contain members that belong to the class but that are not tied to any instance (like Java's static members). Implementing the method there allows you to access your value by calling:

var bar = Types.findByValue(2) ?: error("No Types enum value found for 2")

Note that the returned value is nullable, to account for the possibility that no enum value corresponds to the parameter that was passed in. You can use the elvis operator ?: to handle that case with an error or a default value.

Joffrey
  • 32,348
  • 6
  • 68
  • 100
  • Suggestion: `fun findByValue(value: Int? ) = types[value]` so that it returns null if given a null value. – Paulo Merson Nov 16 '22 at 14:26
  • 1
    I think that would not be the right place to handle null inputs. I have never needed such a thing. Nulls are so easy to deal with in Kotlin, that there is probably an earlier place where the null would have been handled. But that's situational of course – Joffrey Nov 16 '22 at 14:54
  • Situational indeed. In my case such fun is often used for mapping the Entity attribute (of type enum) to/from the corresponding attribute in a JSON doc used in a REST API or the corresponding attribute/column in the database. Many times these JSON and DB attributes can be null. – Paulo Merson Nov 17 '22 at 12:09
6

If you hate declaring for each enum type a companion object{ ... } to achieve EMotorcycleType.fromInt(...). Here's a solution for you.

EnumCaster object:

object EnumCaster {
    inline fun <reified E : Enum<E>> fromInt(value: Int): E {
        return enumValues<E>().first { it.toString().toInt() == value }
    }
}

Enum example:

enum class EMotorcycleType(val value: Int){
    Unknown(0),
    Sport(1),
    SportTouring(2),
    Touring(3),
    Naked(4),
    Enduro(5),
    SuperMoto(6),
    Chopper(7),
    CafeRacer(8),

    .....
    Count(9999);

    override fun toString(): String = value.toString()
}

Usage example 1: Kotlin enum to jni and back

fun getType(): EMotorcycleType = EnumCaster.fromInt(nGetType())
private external fun nGetType(): Int

fun setType(type: EMotorcycleType) = nSetType(type.value)
private external fun nSetType(value: Int)

---- or ----

var type : EMotorcycleType
    get() = EnumCaster.fromInt(nGetType())
    set(value) = nSetType(value.value)

private external fun nGetType(): Int
private external fun nSetType(value: Int)

Usage example 2: Assign to val

val type = EnumCaster.fromInt<EMotorcycleType>(aValidTypeIntValue)

val typeTwo : EMotorcycleType = EnumCaster.fromInt(anotherValidTypeIntValue)
Stoica Mircea
  • 782
  • 10
  • 22
  • 2
    That is unsafe on multiple levels, though. I really wouldn't advise it. In `EnumCaster.fromInt`, there is no type information about the enum `E` that allows you to know that `toString()` was implemented with this hack in mind. For a safer implementation of this idea, you should look at Ji Fang's answer https://stackoverflow.com/a/71578372/1540818 – Joffrey Nov 16 '22 at 13:53
5

A naive way can be:

enum class Types(val value: Int) {
    FOO(1),
    BAR(2),
    FOO_BAR(3);

    companion object {
        fun valueOf(value: Int) = Types.values().find { it.value == value }
    }
}

Then you can use

var bar = Types.valueOf(2)
Geno Chen
  • 4,916
  • 6
  • 21
  • 39
4

Protocol orientated way with type-safety

interface RawRepresentable<T> {
    val rawValue: T
}

inline fun <reified E, T> valueOf(value: T): E? where E : Enum<E>, E: RawRepresentable<T> {
    return enumValues<E>().firstOrNull { it.rawValue == value }
}

enum class Types(override val rawValue: Int): RawRepresentable<Int> {
    FOO(1),
    BAR(2),
    FOO_BAR(3);
}

Usage

val type = valueOf<Type>(2) // BAR(2)

You can use it on non-integer type, too.

Ji Fang
  • 3,288
  • 1
  • 21
  • 18
0

I would build the 'reverse' map ahead of time. Probably not a big improvement, but also not much code.

enum class Test(val value: Int) {
    A(1),
    B(2);

    companion object {
        val reverseValues: Map<Int, Test> = values().associate { it.value to it }
        fun valueFrom(i: Int): Test = reverseValues[i]!!
    }
}

Edit: map...toMap() changed to associate per @hotkey's suggestion.

axblount
  • 2,639
  • 23
  • 27
0

try this...

companion object{
    fun FromInt(v:Int):Type{
        return  Type::class.java.constructors[0].newInstance(v) as Type
    }
}
Hani
  • 149
  • 1
  • 3
  • Hi Hani, welcome to stackoverflow. I suggest to elaborate your answer, while your code should be correct, it is a good idea to explain why and what you are doing there. Thank you. – Giuseppe Garassino Jan 29 '20 at 18:27
  • `Enum` constructors are private for a reason. Even if you could do this (something I really doubt), you don't want to create new instances of an `Enum` outside of the ones defined in it. This code might be OK for standard classes, though. – Igor Rodriguez Jul 14 '21 at 09:47
0

This is for anyone looking for getting the enum from its ordinal or index integer.

enum class MyEnum { RED, GREEN, BLUE }
MyEnum.values()[1] // GREEN

Another solution and its variations:

inline fun <reified T : Enum<T>> enumFromIndex(i: Int) = enumValues<T>()[i]
enumFromIndex<MyEnum>(1) // GREEN
inline fun <reified T : Enum<T>> enumFromIndex(i: Int) = enumValues<T>().getOrNull(i)
enumFromIndex<MyEnum>(3) ?: MyEnum.RED // RED
inline fun <reified T : Enum<T>> enumFromIndex(i: Int, default: T) =
    enumValues<T>().getOrElse(i) { default }
enumFromIndex(2, MyEnum.RED) // BLUE

It is an adapted version of another answer. Also, thanks to Miha_x64 for this answer.

Mahozad
  • 18,032
  • 13
  • 118
  • 133
0

Another option...

enum class Types(val code: Int) {
    FOO(1),
    BAR(2),
    FOO_BAR(3);

    companion object {
        val map = values().associate { it.code to it }

        // Get Type by code with check existing codes and default
        fun getByCode(code: Int, typeDefault_param: Types = FOO): Types {
            return map[code] ?: typeDefault_param
        }
    }
}

fun main() {
    println("get 3: ${Types.getByCode(3)}")
    println("get 10: ${Types.getByCode(10)}")
}

get 3: FOO_BAR
get 10: FOO