Scala superimposes a very elegant class hierarchy over Java's type system, balooning out from Any at the top, into AnyRef and AnyVal to cover Java's Objects and primitives respectively, then finally converging, collapsing the reference types onto Null and all types onto Nothing. As I understand it, Nothing is a subtype of everything; Null a subtype of all subtypes of AnyRef/java.lang.Object. [ see http://www.scala-lang.org/node/128 ]
However, there seem to be a few irregularities, a few places where it does not work to simply think of all Scala types as elements of a seamless type hierarchy. I find this irksome, and want to understand the places where I might be surprised.
So far, I know of a few irregularities:
1) Although Null is a subtype of AnyRef, calling null.isInstanceOf[AnyRef] (or other subtypes of AnyRef) returns false. I suspect this was chosen to be consistent with the behavior of Java's instanceof operator.
2) Everything is covariant to Nothing, regardless of variance annotations. If I have a method that returns a type T that is not marked covariant, I can override that method to return type Nothing. [NOTE: this claim is mistaken, see answers and comments below!]
3) I can't apply isInstanceOf to the type AnyVal [ See Why can AnyVal not be used in an isInstanceOf check? and How to test a value on being AnyVal? ]
4) It is illegal to ask whether something isInstanceOf[Null], which is a perfectly coherent thing to ask (although not particularly necessary, since "myVar == null" would give the same answer)
Are there other examples of irregularities or special cases in Scala's type hierarchy? I feel like these are worth learning and understanding to avoid unwelcome surprises.
trait T[A]; case class C[A]() extends T[A]; val l:T[String] = C[Nothing]()
. It won't work due to invariance. If you change it to+A
it does work. – Kim Stebel