The reason that the constraint B >: A
compiles in your example is that the compiler tries to look for the most specific type that satisfies the constraint. As Int
is your lower bound, and String
and Int
both share a super type Any
, the compiler allows this example to compile.
One thing you have to remember is that Scala runs on the JVM (well, at least the most common distribution). This means that we have type erasure for generic types.
If we compile your example and look at the post erasure phase we see:
class Test extends Object {
def print(x: Object): Unit = {
scala.this.Predef.println(scala.Boolean.box(x.$isInstanceOf[Object]()));
scala.this.Predef.println(scala.Boolean.box(x.$isInstanceOf[Object]()))
};
}
As you see, due to type erase, we're actually comparing x
twice to Object
, which is why you see true
printed twice.
If you want to check for type equality, you'll need to provide implicit evidence of a TypeTag
. A TypeTag
provides type information at runtime. Provided, we can use typeOf
with a =:=
which checks equality of the specific type. We require a TypeTag
either via context bounds, or explicitly via an implicit parameter (for this example, I'll use the former):
scala> :pa
// Entering paste mode (ctrl-D to finish)
import scala.reflect.runtime.universe._
class Test[A: TypeTag] {
def print[B: TypeTag](x: B): Unit = {
println(typeOf[B] =:= typeOf[A])
println(typeOf[B] =:= typeOf[B])
}
}
// Exiting paste mode, now interpreting.
import scala.reflect.runtime.universe._
defined class Test
scala> val t = new Test[Int]
t: Test[Int] = Test@7276c8cd
scala> t.print("s")
false
true