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")
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.
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.
valueOf(Class<T> enumType, String name) Returns the enum constant of the specified enum type with the specified name.
By default enums have their own string values, we can also assign some custom values to enums.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With