Let's suppose we have a generic class Container
:
case class Container[+A](value: A)
We then want to pattern match a Container
with a Double
and a Container
of Any
:
val double = Container(3.3) var container: Container[Any] = double
To do this, we would normally write:
container match { case c: Container[String] => println(c.value.toUpperCase) case c: Container[Double] => println(math.sqrt(c.value)) case _ => println("_") }
However, the compiler gives two warnings, one for each of the first two cases. For example, the first warning says: "non-variable type argument String in type pattern Container[String] is unchecked since it is eliminated by erasure". Because of the erasure, it is impossible during runtime to distinguish between different kinds of containers and the first catch will be matched. As a consequence, container of type Container[Double]
will be matched by the first case, which catches Container[String]
objects, so toUpperCase
method will be called on a Double
and a java.lang.ClassCastException
will be thrown.
How to match a Container
parametrized by a particular type?
Notes. 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.
Pattern matching is a way of checking the given sequence of tokens for the presence of the specific pattern. It is the most widely used feature in Scala. It is a technique for checking a value against a pattern. It is similar to the switch statement of Java and C.
To use a generic class, put the type in the square brackets in place of A . Class Apple and Banana both extend Fruit so we can push instances apple and banana onto the stack of Fruit . Note: subtyping of generic types is *invariant*.
It is defined in Scala's root class Any and therefore is available for all objects. 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. A symbol => is used to separate the pattern from the expressions.
In general rarry's answer is correct, for your case however it can be simplified, because your container only contains a single value of a generic type, so you can match on that value's type directly:
container match { case Container(x: String) => println("string") case Container(x: Double) => println("double") case _ => println("w00t") }
Maybe this will help
def matchContainer[A: Manifest](c: Container[A]) = c match { case c: Container[String] if manifest <:< manifest[String] => println(c.value.toUpperCase) case c: Container[Double] if manifest <:< manifest[Double] => println(math.sqrt(c.value)) case c: Container[_] => println("other") }
Edit:
As Impredicative pointed out, Manifest is deprecated. Instead you could do the following:
import reflect.runtime.universe._ def matchContainer[A: TypeTag](c: Container[A]) = c match { case c: Container[String] if typeOf[A] <:< typeOf[String] => println("string: " + c.value.toUpperCase) case c: Container[Double] if typeOf[A] <:< typeOf[Double] => println("double" + math.sqrt(c.value)) case c: Container[_] => println("other") }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With