Last active
August 29, 2015 14:21
-
-
Save kumarishan/f38fec3778fa92c55a07 to your computer and use it in GitHub Desktop.
Function matching over Type Parameter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment