Scala function which extracts Class[_] out of generic

68 Views Asked by At

So what I want to do:

def foo(clazz: Class[_]): Unit = {
  val int = classOf[Int]
  val str = classOf[String]
  val opt = classOf[Option[_]]
  clazz match {
    case `int` => println("INT!")
    case `str` => println("STRING!")
    case `opt` => foo(clazz.getTheTypeOutOfGeneric) // <- I think you get the idea 
  }
}

For the given clazz: Class[_ :< Option[_]] I would want to find type with which it was created, so in the end I would get INT! when running with type Option[Int]. How can I do it? Do I have to use reflections?

2

There are 2 best solutions below

4
On

Simply put: Class does not carry any information about type parameters.

So for example

classOf[Option[Int]] eq classOf[Option[String]]

will return true. Note that the operator eq checks for referential equality, so these two are actually the exact same object!

Try it out!

Even with reflection you won't be able to get information which is just not there.


I would recommend to move away from using Class values and instead use type parameters and type classes for ad-hoc polymorphism. The exact solution depends on what you are effectively trying to do.

But, here is an example on how type classes work:

def get[F[_], A](a: F[A])(implicit ex: Extractor[A]): Unit = ex.extract

trait Extractor[A] {
  def extract: Unit
}

implicit val intExtractor = new Extractor[Int] {
  def extract: Unit = println("I am doing something for Int!")
}

implicit val strExtractor = new Extractor[String] {
  def extract: Unit = println("I am doing something for String!")
}

val a = Some(1)

get(a)

Output:

I am doing something for Int!

Try it out!

For more information I would recommend reading The Type Astronaut’s Guide to Shapeless, which (apart from teaching you about Shapeless) helps you understand more general aspects of generic programming in Scala.

I hope this helps.

0
On

You can't that information from Class because of type erasure. You would need a ClassTag for that:

import scala.reflect.runtime.universe._
def foo[T : TypeTag](clazz: Class[T]) = clazz match {
   ...
   case `opt` => foo(typeOf[T].typeArgs.head)
   ...
}

But before you do this, do check out Type and TypeTag, and other goodies in scala.reflect.runtime.universe - more than likely, you will find a simpler and a more elegant way to implement what you need with that stuff, than passing raw Class'es around.