In Scala's upper bound concept, the given type or its super type can be passed. For example, in the below method S is the type and A is the parameter we pass. This method accepts all the values present in Scala's type system actually. S, S subtype and its super type. This is due to the fact that all types extends Any type.
def method[A >: S](a:A) = { ... }
Then why can't we write all the upper bound notations as Any (which is the universal type in Scala). The above definition can be re-written as:
def met(a:Any) = { ... }
This is easy to understand.
What sort of advantage the upperbound brings in ?
Thanks!
It allows you to lose less type information than you would by going to Any
.
For example If you have Dog and Cat inherit Animal, this works:
val maybeDog: Option[Dog] = ???
val pet = maybeDog.getOrElse(Cat())
Because getOrElse
has a signature of def getOrElse[B >: A](default: => B): B
, it is inferred that B
is Animal
(as the least upper bound of Cat and Dog), so the static type of val pet
is Animal
. If it was using Any
, the result would require unsafe casting to work further. If it was not introducing a new type parameter altogether, you would be forced to write (maybeDog: Option[Animal]).getOrElse(Cat())
to achieve the same unification.
Additionally, it restricts the implementation to not do something completely silly. For example, this typechecks:
def getOrElse[A](option: Option[A])(default: => Any): Any = 42 // Int is Any, so why not?
While this doesn't:
def getOrElse[A, B >: A](option: Option[A])(default: => B): B = 42
Because while anything can go as B
, that doesn't imply that Int is always a subtype of B.