Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I match classes in a Scala "match" statement?

Tags:

scala

How can I use a "match" statement to identify the value of a class variable? The following is invalid, and I can't find an acceptable variant -- other than if ... else if ... else ...

val c: Class[_] = classOf[Int]
val what = c match { case classOf[Int] => "int!"; case classOf[Float] => "float!" }

The compiler complains: error: not found: type classOf

And of course, I can't use Class[Int] because that type information is erased:

c match { case Class[Int] => "int!"; case Class[Float] => "float!" }
error: type Class of type Class does not take type parameters.

I've also tried variants like Int.class, all to no avail. (And I don't really want to convert to strings: I feel it's important to have the compiler catch renamed/moved classes.)

Am I being dense, or have I stumbled into a Scala blind spot?

like image 486
Tim Avatar asked Aug 23 '11 06:08

Tim


People also ask

How do you use match function in Scala?

Using if expressions in case statements First, another example of how to match ranges of numbers: i match { case a if 0 to 9 contains a => println("0-9 range: " + a) case b if 10 to 19 contains b => println("10-19 range: " + b) case c if 20 to 29 contains c => println("20-29 range: " + c) case _ => println("Hmmm...") }

Does Scala have pattern matching?

Scala's pattern matching statement is most useful for matching on algebraic types expressed via case classes. Scala also allows the definition of patterns independently of case classes, using unapply methods in extractor objects.

Which method of case class allows using objects in pattern matching?

The match method takes a number of cases as an argument. Each alternative takes a pattern and one or more expressions that will be performed if the pattern matches.

What is case _ in Scala?

case _ => does not check for the type, so it would match anything (similar to default in Java). case _ : ByteType matches only an instance of ByteType . It is the same like case x : ByteType , just without binding the casted matched object to a name x .


3 Answers

The verbose case comparison works:

val what = c match {   case q if q == classOf[Int] => "int!"   case q if q == classOf[Float] => "float!" } 

Of course, being a lower-case identifier, classOf should not work directly in a case statement anyway. However, neither does an escaped

case `classOf`[Int] 

work in this case, so you’ll have to go with the if-guard.

like image 149
Debilski Avatar answered Oct 11 '22 16:10

Debilski


You can match on class values if you create a stable identifier (ie. a val) for them,

scala> val c: Class[_] = classOf[Int]
c: Class[_] = int

scala> val ClassOfInt = classOf[Int]
ClassOfInt: java.lang.Class[Int] = int

scala> val ClassOfFloat = classOf[Float]
ClassOfFloat: java.lang.Class[Float] = float

scala> val what = c match {
     |     case ClassOfInt => "int!"
     |     case ClassOfFloat => "float!"
     | }
what: String = int!

Note that you can't match on type (ie. Class[Int]) because erasure means that the different type instantiations of Class[T] are indistinguishable at runtime ... hence the warning below

scala> val what = c match {
     |     case _: Class[Int] => "int!"
     |     case _: Class[Float] => "float!"
     | }
warning: there were 2 unchecked warnings; re-run with -unchecked for details
what: java.lang.String = int!
like image 35
Miles Sabin Avatar answered Oct 11 '22 17:10

Miles Sabin


I encountered the same problem and placing the class in a 'stable identifier' wasn't that practical. I found the next best thing was to have tidy 'else if' statements.

Using this method:

private def is[T <: AnyRef : Manifest](implicit cls: Class[_]) = 
    cls == manifest[T].runtimeClass

I can write:

  implicit val arg = cls   
  if (is[ClassA]) ...
  else if (is[ClassB]) ...
  ...
  else throw new IllegalArgumentException("Unknown class: " + cls)
like image 34
Peter L Avatar answered Oct 11 '22 16:10

Peter L