import scala.reflect._ case class A(a: Int) case class B[T](b: T) object Example1 { // [IMP] the names of vals has to be capital case // so that they can be used in case statement. // case statement requires stable identifiers and therefore // in order to match, classOf[String] cannot be directly // used. val AClass = classOf[A] val StringClass = classOf[String] val BClass = classOf[B[Int]] val ListClass = classOf[List[String]] def foo(clazz: Class[_]) = clazz match { case AClass => "it's A" case BClass => "it's B" case StringClass => "it's String" case ListClass => "it's list" } def fooBetter[T: ClassTag] = implicitly[ClassTag[T]].runtimeClass match { case AClass => "it's A" case BClass => "it's B" case StringClass => "it's String" case ListClass => "it's list" } def bar[T: ClassTag](a: Int) = implicitly[ClassTag[T]].runtimeClass match { case AClass => s"it's A and $a" case BClass => s"it's B and $a" case StringClass => s"it's String and $a" case ListClass => s"it's list and $a" } def notSoGoodBar(a: Int, clazz: Class[_]) = clazz match { case AClass => s"it's A and $a" case BClass => s"it's B and $a" case StringClass => s"it's String and $a" case ListClass => s"it's list and $a" } def fromString(s: String): Class[_] = s match { case "a" => classOf[A] case "b" => classOf[B[Any]] case "string" => classOf[String] case "list" => classOf[List[Any]] } def helo(args: Array[String]) { println(foo(classOf[String])) // output -> it's String println(foo(classOf[B[Int]])) // output -> it's B println(foo(classOf[A])) // output -> it's A println(foo(classOf[List[String]])) // output -> it's list // If you passed classOf[B[String]] this will // still give "it's B" even though we used // BClass which is classOf[B[Int]]. // This happens because T of B[T] is erased by the compiler // and classOf only represent the erased or runtime // type i.e. B println(foo(classOf[B[String]])) // output -> it's B // you call also verify like this println(classOf[B[String]] eq classOf[B[Int]]) // output -> true // Now fooBetter makes the above code even more prettier // by using ClassTag. // This allows us to pass only the type in Type Parameter // instead of passing Class[_] instance println(fooBetter[String]) // output -> it's String println(fooBetter[B[Int]]) // output -> it's B println(fooBetter[A]) // output -> it's A println(fooBetter[List[String]]) // output -> it's list // This becomes even more useful when u have other parameters to // pass too like in bar println(bar[String](1)) // output -> it's String and 1 println(bar[B[Int]](1)) // output -> it's B and 1 println(bar[A](1)) // output -> it's A and 1 println(bar[List[String]](1)) // output -> it's list and 1 // certainly better than notSoGoodBar // Therefore as long as only erased type infomation // is needed to match and perform action. Then classOf // or better yet ClassTag is the best way to do // classOf vs ClassTag route // Now what if you had a string and using that you // want to retriev the type to be used and to call // other classes that take action baed on type parameter // First using classOf based methods // This function takes a string, gets Class[_] // object and passes it to foo def aloha1(s: String) = { val clazz = fromString(s) foo(clazz) } // But if u wanted to user the fooBetter, i.e // which uses the ClassTag on T type parameter // same thing can be done like this def aloha2(s: String) = { implicit val tag = ClassTag(fromString(s)) // here tag's type is ClassTag[Nothing] fooBetter // this will automatically take the above implicit val } // Well from first glance aloha1 appears to simple and faster. // So lets check it. val ps = Array("a", "b", "string", "list") val itr = 1000000 val params = Array.fill(itr)(ps(Random.nextInt(4))) var startTime = System.currentTimeMillis for(i <- 0 until itr) aloha1(params(i)) println(s"Finished in ${System.currentTimeMillis - startTime}") startTime = System.currentTimeMillis for(i <- 0 until itr) aloha2(params(i)) println(s"Finished in ${System.currentTimeMillis - startTime}") // The results of the above run proves that classOf way is more // perfomant but its not really much big of a difference like 10 milliseconds // in the above run } }