Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Enum.valueOf from Scala?

I need to get a Java enum value from a string given Enum's Class instance. I tried code like below, but I'm getting "unbound wildcard type" compilation error. Seems, I need to do something with existential types, forSome {} or something, but I can't get how to do it right.

val paramClass = method.getParameterTypes()(0)
val value = paramClass match {
  case _ if classOf[Enum[_]].isAssignableFrom(paramClass) => Enum.valueOf[_ <: Enum[_]](paramClass.asInstanceOf[Class[_ <: Enum[_]]], "MYENUM")
like image 875
nau Avatar asked May 19 '11 08:05

nau


People also ask

How do you use enum value?

valueOf() method returns the enum constant of the specified enumtype with the specified name. The name must match exactly an identifier used to declare an enum constant in this type.

Is there enum in Scala?

In Scala, there is no enum keyword unlike Java or C. Scala provides an Enumeration class which we can extend in order to create our enumerations. Every Enumeration constant represents an object of type Enumeration. Enumeration values are defined as val members of the evaluation.

What is valueOf enum return?

valueOf(Class<T> enumType, String name) Returns the enum constant of the specified enum type with the specified name.

Can enums have a value?

By default enums have their own string values, we can also assign some custom values to enums.


1 Answers

Hmm, tough one. I have a working solution, but I find it ugly. I'll be interested in any more elegant approach!

def enumValueOf[T <: Enum[T]](cls: Class[_], stringValue: String): Enum[_] =
  Enum.valueOf(cls.asInstanceOf[Class[T]], stringValue).asInstanceOf[Enum[_]]

val value = paramClass match {
  case _ if classOf[Enum[_]].isAssignableFrom(paramClass) => enumValueOf(paramClass, "MYENUM")
  case _ => // other cases
}

The reason why I think we need this complexity…

We need the compiler to believe that the Class[_] we have is actually a Class[T <: Enum[T]] (so of course, a preliminary test that this is indeed a Java enum — as done in your code — is needed). So we cast cls to Class[T], where T was inferred by the compiler to be <: Enum[T]. But the compiler still has to find a suitable T, and defaults to Nothing here. So, as far as the compiler is concerned, cls.asInstanceOf[Class[T]] is a Class[Nothing]. This is temporarily OK since it can be used to call Enum.valueOf — the problem is that the inferred return type of valueOf is then, naturally, Nothing as well. And here we have a problem, because the compiler will insert an exception when we try to actually use an instance of type Nothing. So, we finally cast the return value of valueOf to an Enum[_].

The trick is then to always let the compiler infer the type argument to enumValueOf and never try to specify it ourselves (since we're not supposed to know it anyway) — and thus to extract the call to Enum.valueOf in another method, giving the compiler a chance to bind a T <: Enum[T].

As I said, I'm not very happy with this solution, which looks way more complicated than it should be…

Update: I've simplified the code slightly.

like image 145
Jean-Philippe Pellet Avatar answered Oct 18 '22 03:10

Jean-Philippe Pellet