I have the following class hierarchy:
class A class B extends A class C extends A
then, there is another class which takes instances of these classes and there is a method, in which two cases of pattern-matching are possible like this:
class D (one: A, two: A) { def work { (one, two) match { case (o, t): (B, B) => ... blablabla case (o, t): (B, C) => ... blablabla case _ => } } }
However, when it should resolve the matching in favor of the second case (B, C)
, it tries resolving it as (B, B)
and comes up with the class cast exception that C cannot be cast to B
. Why? What to do? How can I come around this?
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.
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.
Your syntax isn't quite right (doesn't compile).
This works though:
object Matcher extends App { class A class B extends A class C extends A class D(one: A, two: A) { def work { (one, two) match { case (o: B, t: B) => println("B") case (o: B, t: C) => println("C") case _ => } } } val d1 = new D(new B, new B) val d2 = new D(new B, new C) d1.work //B d2.work //C }
The problem, as always, is erased types. (B,C)
is syntactic sugar for Tuple2[B,C]
, which is erased to Tuple2
at runtime. The case statement verifies that (B,C)
matches Tuple2
, but then fails to cast it.
In your case, the easiest solution would be to match against 'one' and 'two' individually, rather than wrapping them in a tuple:
one match { case o : B => two match { case p : C => ... case p : B => ... } ... }
It's not so pretty, but it won't suffer from the same problems.
Edit: Actually, I'd go with Brian Smith's solution - matching inside the tuple rather than outside. It avoids the problem in a similar way, but looks nicer.
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