0

I'm playing with Scala's type system with the following example:

scala> sealed trait A
defined trait A

scala> case class A1(n: Int) extends A
defined class A1

scala> case class A2(n: Int) extends A
defined class A2

scala> case class MyCase[+P <: A](a: A, y: String)
defined class MyCase

scala> val l = List(MyCase[A1](A1(1), "A1"), MyCase[A2](A2(2), "A2"))

Now, when I do the following:

scala> l.head.asInstanceOf[MyCase[A2]]
res2: MyCase[A2] = MyCase(A1(1),A1)

How can a MyCase[A1] instance be assigned to a MyCase[A2] reference? I mean MyCase[A1] and MyCase[A2] are at the same level in the object hierarchy!

Ben Reich
  • 16,222
  • 2
  • 38
  • 59
joesan
  • 13,963
  • 27
  • 95
  • 232
  • Yes, sorry for the confusion! My question was actually something. I modified my post above – joesan Jul 06 '15 at 14:11
  • What's the point of having the type parameter P at all, if it's not used? – Seth Tisue Jul 06 '15 at 14:15
  • 2
    *"how can `MyCase[A1]` object be assigned to a `MyCase[A2]` reference?"* -- Because you're *forcing* it with a type cast. – Michael Zajac Jul 06 '15 at 14:16
  • 1
    `asInstanceOf` is an unsafe operation. You're telling the compiler "trust me, I know what I'm doing". If the cast is invalid, you might get a runtime error immediately, or you might not — you might just end up having problems later. `asInstanceOf` is never used in normal Scala code. – Seth Tisue Jul 06 '15 at 14:16

1 Answers1

2

As I understand you're wondering why you didn't get an error here in runtime, when you did l.head.asInstanceOf[MyCase[A2]]. So the answer is simple - type erasure. For JVM both types are equal as it ignores generic parameter - it simply doesn't know about them as they exist on compiler level only, so only compiler can give you an exception:

scala> val mcA2: MyCase[A2] = l.head
<console>:15: error: type mismatch;
 found   : MyCase[Product with Serializable with A]
 required: MyCase[A2]
       val mcA2: MyCase[A2] = l.head
                                ^

However, asInstanceOf ignores this check. So when you do

case class MyCase[+P <: A](a: P) //P instead of A
scala> val k = MyCase[A1](A1(0)).asInstanceOf[MyCase[A2]]
k: MyCase[A2] = MyCase(A1(0))

scala> val a: A2 = k.a
java.lang.ClassCastException: A1 cannot be cast to A2
... 33 elided

But in your example - you never really used P in runtime (and it's impossible), so you never gonna get ClassCastException. Actually, under these conditions (no real instances), you can do type-level copy like here, but it safer to use traits for that.

Community
  • 1
  • 1
dk14
  • 22,206
  • 4
  • 51
  • 88
  • This exercise was just for me to understand Scala's Bounded types. I'm intrigued by the example thus far. Why would I then concern myself with bounded types? What are some typical use cases where I could use Bounded types? – joesan Jul 06 '15 at 14:41
  • It's simple, you can't even create `MyCase[String]` for instance, because it's bounded with `A` (without `asInstanceOf` of course). Covariance (`A >: B => M[A] >: M[B]`) is absolutely ortogonal to it, as long as `asInstanceOf`, it's just not related. – dk14 Jul 06 '15 at 14:48