2

Could the following Java code:

public <T extends Enum<T>> T foo(Class<T> clazz) {

  return clazz.getEnumConstants()[0];
}

public void bar(Class<?> clazz) {

    if (Enum.class.isAssignableFrom(clazz)) {
        System.out.println(foo(clazz.asSubclass(Enum.class)));
    } else if (String.class.isAssignableFrom(clazz)) {
        System.out.println("Meow");
    }
}

bar(MyEnum.class) // prints the first value of MyEnum
bar(String.class) // prints Meow

be translated to Scala:

bar[MyEnum]()
bar[String]()

Enum is just an example of a class that follows the T extends Wrapper[T] pattern, and foo could have simply returned the name of the class (or perform any other kind of logic which requires reflective data that is only available in my "Wrapper" class).

I tried to make it work in Scala with TypeTag but failed; I got all sort of compilation errors, such as this: inferred type arguments [?0] do not conform to method foo's type parameter bounds [E <: Enum[E]]

Eyal Roth
  • 3,369
  • 6
  • 32
  • 37

2 Answers2

3

There is quite likely a better way to do what you actually need, but:

import language.existentials
import reflect.ClassTag

def foo[T <: Enum[T]](implicit ct: ClassTag[T]) = ct

def bar[T](implicit ct: ClassTag[T]) = {
  val clazz = ct.runtimeClass

  if (classOf[Enum[_]].isAssignableFrom(clazz)) {
    println(foo(ct.asInstanceOf[ClassTag[A] forSome { type A <: Enum[A] }]))
  } else {
    println("not a enum")
  }
}
Alexey Romanov
  • 154,018
  • 31
  • 276
  • 433
  • I was just editing my answer with the "literal translation" but since you posted it I'll leave it to you :) +1 for the existential type though, maybe it's what he wanted to know – Giovanni Caporaletti Apr 25 '16 at 17:32
  • Yes! I tried something similar, and my `asInstanceOf` had a simple `Class[_ <: :p="" a="" actually="" and="" better="" but="" calling="" enum="" existential="" explained="" fix="" i="" it="" java="" knew="" lazy="" method="" need="" representation="" requires="" that="" to="" too="" type="" was="" won="" work=""> – Eyal Roth Apr 25 '16 at 18:14
1

You can try a typeclass approach, so that everything is resolved at compile-time, no reflection involved:

import scala.reflect.ClassTag

trait DoSomething[T] {
  def apply(): Unit
}

object DoSomething {
  implicit def enumDoSomething[E <: Enum[E]](implicit ct: ClassTag[E]) = new DoSomething[E] {
    def apply() = println(ct.runtimeClass.getEnumConstants()(0))
  }

  implicit object stringDoSomething extends DoSomething[String] {
    def apply() = println("Meow")
  }
}

object Test extends App {
  def foo[A](implicit doSomething: DoSomething[A]) = doSomething()

  foo[java.util.concurrent.TimeUnit]
  foo[String]
}

This way you don't have an if-else and you have a better separation of concerns.

if you want a "default" case, you can have a catch-all implicit.

import scala.reflect.ClassTag

trait DoSomething[T] {
  def apply(): Unit
}

object DoSomething {

  implicit def enumDoSomething[E <: Enum[E]](implicit ct: ClassTag[E]) = new DoSomething[E] {
    def apply() = println(ct.runtimeClass.getEnumConstants()(0))
  }

  implicit object stringDoSomething extends DoSomething[String] {
    def apply() = println("Meow")
  }

  implicit def catchAll[T] = new DoSomething[T] {
    def apply() = {
      println("test") // some default case
    }
  }
}

object Test extends App {
  def foo[A](implicit doSomething: DoSomething[A]) = doSomething()

  foo[java.util.concurrent.TimeUnit] // prints NANOSECONDS
  foo[String] // prints Meow
  foo[Long] // prints test
}

If you're interested in how scala finds implicits, take a look at this

Community
  • 1
  • 1
Giovanni Caporaletti
  • 4,996
  • 2
  • 24
  • 37
  • Yes, this is possible, but isn't what I asked for; I probably wasn't clear enough. `bar` type parameter must not be bound (simply `A`), and it must use `foo` which must have such bound type parameter. Moreover, your `bar` method won't compile with `String` (as in my example). – Eyal Roth Apr 25 '16 at 16:59
  • Oh ok, I see now, let me try something else – Giovanni Caporaletti Apr 25 '16 at 17:12
  • 1) It doesn't compile for me. Have you tried it? 2) I'm looking for a way to do that without changing `foo` as much (my `foo` = your `enumDoSomething`). – Eyal Roth Apr 25 '16 at 18:05
  • Umh yes I did O_o Let me check it -> It works, have you imported ClassTag? I edited the answer to add the import. I copy-pasted exactly what you see in the answer and it runs and prints what's expected – Giovanni Caporaletti Apr 25 '16 at 18:37
  • Are you on scala 2.10 by any chance? That could be why. – Giovanni Caporaletti Apr 25 '16 at 18:52
  • No, the problem was the order of the objects in the file. My `Test` object was before `DoSomething`. – Eyal Roth Apr 26 '16 at 10:50