How to use Enum.valueOf from Scala?
-
15-11-2019 - |
Frage
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")
Lösung
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.