1

I am trying to understand why Scala runtime does not complain while i try to cast one subclass into another. And how do i make scala to complain.

I have following inheritance in my application

trait I {
val i: String
}

trait K extends I {
val k: String
}

case class S(override val i:String, override val k: String, val s: String) extends K

case class M(override val i: String, val m: String) extends I

As per my understanding following method call should have thrown a class cast exception. But it works perfectly fine. listM holds list of S. And i am able to create instance of MakeM such that its member contains List of S instead of List of M. This is very confusing. How does this work? Is scala being smart in converting one subclass into another subclass. How do i enforce scala to complain when sub classes are being casted into one another.

case class MakeM(val data:String, val list: Seq[M])


object Test {

  def main(args: Array[String]): Unit = {
    val listOfS = Some(List(S("i1", "k1", "s1"), S("i2", "k2", "s2")))
    val listOfM:Seq[M] = listOfS.get.asInstanceOf[Seq[M]] //this works , why?
    val m1 = MakeM("some data", listOfM); //this works. why?
    //val m2 = MakeM("some data", listOfS.get); //this fails
    println(s"$m1");
  }

}

Actual Output

MakeM(some data,List(S(i1,k1,s1), S(i2,k2,s2)))

Expected Output

 Class Cast exception.
konquestor
  • 1,308
  • 3
  • 15
  • 29
  • @jwvh my bad i wrote a pseudo code. I have updated it to compilable and runnable code. Kindly revert the negative point so the question gets rightful attention. – konquestor Aug 02 '16 at 02:40
  • 2
    This seems like a lot more code than needed to explain the question. Can it be reduced to a minimal example? – Chris Martin Aug 02 '16 at 02:53
  • @konquestor, I'll withdraw my comment since you've fixed the posted code (thank you), but I didn't down-vote the question so I can't reverse that. – jwvh Aug 02 '16 at 03:00
  • @ChrisMartin i have shortened the code. can you help me understand whats going on? – konquestor Aug 02 '16 at 03:07
  • Sorry misread the question. If the code works in runtime, try accessing `list(0).m` or even just `list(0)` in the `MakeM` class. Maybe the cast works because it only checks the list class itself and not inner-elements; `List` inherits from `Seq` – Doc Aug 02 '16 at 03:43
  • 2
    Duplicates http://stackoverflow.com/questions/26089390/why-asinstanceof-doesnt-throw-a-classcastexception and http://stackoverflow.com/questions/34204473/why-does-asinstanceof-sometimes-throw-and-sometimes-not and others like http://stackoverflow.com/questions/33153502/why-does-1-asinstanceoft-not-throw-classcastexception – som-snytt Aug 02 '16 at 04:20

2 Answers2

2

Much of what doesn't compile is a violation of the type system. An example is your line of code with the this fails comment. It fails due to a type mismatch. Using asInstanceOf[] overrides the type system.

"word".asInstanceOf[Float]  // this compiles, but it won't run

In your code, if you were to do something like this...

m1.list.head.m  // this compiles because you told the compiler m1 is type MakeM

...it will throw a runtime error because m1 isn't really type MakeM. We learn that only at runtime because you told the compiler to pretend otherwise.

This runs println(s"$m1") (Scala code doesn't need the semicolon) because the underlying toString method doesn't impact the type inconsistency.

It sounds like you want the compiler to disallow unsafe casting, but by making the cast you are taking away the compiler's main safety-detector.

jwvh
  • 50,871
  • 7
  • 38
  • 64
1

I think I have figured a bit out, it only checks the List, not List[insideType]. So it works for doing List.asInstance(Seq[Anytype]) because Seq works for List. However, if you want to test to the inside type level, it will gives you cast exception.

val listTemp:Seq[K] = listOfS.get.asInstanceOf[Seq[K]]
val listTemp2:Seq[Double] = listOfS.get.asInstanceOf[Seq[Double]]
println(listTemp(1)) //S(i2,k2,s2)
println(listTemp(1).k) //Exception in thread "main" java.lang.ClassCastException: S cannot be cast to K
println(listTemp2.isInstanceOf[Seq[S]]) // true, Seq[Double] is instance of Seq[S], only checks Seq

Please refer to this post, it has some similar problem may interest you: Scala isInstanceOf and type erasure

"""

scala> val l = List("foo")
l: List[String] = List(foo)

scala> l.isInstanceOf[List[String]]
res0: Boolean = true

scala> l.isInstanceOf[List[Int]]
<console>:9: warning: fruitless type test: a value of type List[String] cannot also be a List[Int] (the underlying of List[Int]) (but still might match its erasure)
              l.isInstanceOf[List[Int]]
                            ^
res1: Boolean = true

"""

Community
  • 1
  • 1
LearnToDie
  • 41
  • 4