Kotlin: Using enums with when

27.1k views Asked by At

Is there any way to cast a when argument to an enum?

 enum class PaymentStatus(val value: Int) {
     PAID(1),
     UNPAID(2) 
 }

fun f(x: Int) {
   val foo = when (x) {
     PaymentStatus.PAID -> "PAID"
     PaymentStatus.UNPAID -> "UNPAID"
   }
}

The above example will not work, as x is int and the values provided are the enum, if I go by PaymentStatus.PAID.value it would work but then I don't get the benefit of when (full coverage), and

when (x as PaymentStatus)

does not work.

Any one have any ideas to make this work?

5

There are 5 answers

2
Sergio On BEST ANSWER

If you need to check a value you can do something like this:

fun f(x: Int) {
    val foo = when (x) {
        PaymentStatus.PAID.value -> "PAID"
        PaymentStatus.UNPAID.value -> "UNPAID"

        else -> throw IllegalStateException()
    }
}

Or you can create factory method create in the companion object of enum class:

enum class PaymentStatus(val value: Int) {
    PAID(1),
    UNPAID(2);

    companion object {
        fun create(x: Int): PaymentStatus {
            return when (x) {
                1 -> PAID
                2 -> UNPAID
                else -> throw IllegalStateException()
            }
        }
    }
}

fun f(x: Int) {
    val foo = when (PaymentStatus.create(x)) {
        PaymentStatus.PAID -> "PAID"
        PaymentStatus.UNPAID -> "UNPAID"
    }
}
0
Hamdy Abd El Fattah On

define your enum

 enum class Images {
        ID_PHOTO, PROFILE_IMAGE, IBAN_IMAGE, LICENSE_IMAGE
    }

define object from your enum

lateinit var selectedImage: Images

use selectedImage object from your enum with when

when (selectedImage) {
  Images.ID_PHOTO -> binding.idPhoto.setImageURI(uri)
  Images.PROFILE_IMAGE -> binding.profileImage.setImageURI(uri)
  Images.LICENSE_IMAGE -> binding.licenseImage.setImageURI(uri) 
  Images.IBAN_IMAGE -> binding.ibanImage.setImageURI(uri)
                    
}

set value for object

private fun pickImageFromGallery(image: Images) {
    selectedImage = image

}
0
GarouDan On

A possible workaround to use when with an enum is the following (maybe it will not target the question entirely but I think is a good idea to have it here as a reference):

package com.company.my_package

import com.company.my_package.MyEnum.*

enum class MyEnum {
    ENUM_ITEM_1,
    ENUM_ITEM_2,
    ENUM_ITEM_3
}

val myCommand1 = { input: Any? -> input.toString() }
val myCommand2 = { input: Any? -> input.toString() }
val myCommand3 = { input: Any? -> input.toString() }
val myElseCommand = { input: Any? -> input.toString() }

fun main() {
    val myValue = null

    when {
        ENUM_ITEM_1 == myValue -> myCommand1(myValue)
        ENUM_ITEM_2 == myValue -> myCommand2(myValue)
        ENUM_ITEM_3 == myValue -> myCommand3(myValue)
        else -> myElseCommand(myValue)
    }
}
0
Roland On

It basically depends on how you want to solve the identification of the appropriate enum value. The rest is probably easy enough.

Here are some variants to solve that:

  1. extension function to PaymentStatus.Companion (or integrate the function into the PaymentStatus.Companion):

    fun PaymentStatus.Companion.fromValue(i : Int) = PaymentStatus.values().single { it.value = i } // or if you want another fallback, just use singleOrNull and add ?: with an appropriate default value
    

    Usage of it in a when:

    fun f(x : Int) = when (PaymentStatus.fromValue(x)) {
      PAID -> "PAID" // or PAID.name()
      UNPAID -> "unpaid" //...
    }
    
  2. using a generic function for all your enums

    inline fun <reified T : Enum<T>> identifyFrom(identifier : (T) -> Boolean) = T::class.java.enumConstants.single(identifier) // or again: singleOrNull ?: throw IllegalArgumentException maybe?
    

    with the following usage then:

    fun f(x : Int) = when (identifyFrom<PaymentStatus> { it.value = x }) {
      PAID -> "PAID"
      UNPAID -> "UNPAID"
    }
    

    this variant clearly has the benefit that it can be reused for basically any enum where you want to get a value based on some property or properties

  3. using when to identify the appropriate enum:

    fun PaymentStatus.Companion.fromValue(i : Int) = when (i) {
      1 -> PAID
      2 -> UNPAID
      else -> IllegalArgumentException("$i is not a valid value for PaymentStatus")
    }
    

    same usage as with the first example. However: I wouldn't use this approach except you have a really good reason for it. The reason I wouldn't use it: it requires you to always remember to adapt both, the enum value and its corresponding counterpart in the fromValue-function. So you always have to update the values (at least) twice ;-)

4
Willi Mentzel On

You don't need when in this particular use-case.

Since your goal is to get the name of the enum element having a specific value x, you can iterate over the elements of PaymentStatus like that and pick the matching element using firstOrNull:

fun getStatusWithValue(x: Int) = PaymentStatus.values().firstOrNull {
     it.value == x
}?.toString()

println(getStatusWithValue(2)) // "UNPAID"

Calling toString() on an enum element will return its name.

Edit: Since you don't want the code to compile when a new PaymentStatus is added, you can just use an exhaustive when:

fun paymentStatusNumToString(x: Int): String {
  val status = PaymentStatus.values().first { it.value == x }

  // when must be exhaustive here, because we don't use an else branch
  return when(status) {
    PaymentStatus.PAID -> "PAID" // you could use status.toString() here too
    PaymentStatus.UNPAID -> "UNPAID"
  }
}