Scala Tutorial
Basics
Control Statements
OOP Concepts
Parameterized - Type
Exceptions
Scala Annotation
Methods
String
Scala Packages
Scala Trait
Collections
Scala Options
Miscellaneous Topics
In Scala, type bounds limit the type parameters to a certain range. An upper bound for a type parameter ensures that a type can only be a subtype of a specified type.
The concept of upper bounds is frequently used when you want to ensure that the type arguments passed to a generic class or method extend a certain base type.
You use the <:
symbol to denote an upper bound.
Let's say we have an abstract class Animal
with a method speak
, and we have two subclasses, Dog
and Cat
.
abstract class Animal { def speak: String } class Dog extends Animal { def speak: String = "Woof!" } class Cat extends Animal { def speak: String = "Meow!" }
Now, let's create a generic class PetContainer
which should only accept types that are subtypes of Animal
:
class PetContainer[A <: Animal](val pet: A) { def petSpeak: String = pet.speak } val dogContainer = new PetContainer(new Dog) println(dogContainer.petSpeak) // Woof! val catContainer = new PetContainer(new Cat) println(catContainer.petSpeak) // Meow!
Here, A <: Animal
implies that the type A
must be a subtype of Animal
. So, the PetContainer
class can only be instantiated with types that are subtypes of Animal
.
If you try to instantiate PetContainer
with a type that is not a subtype of Animal
, you'll get a compile-time error:
class Car { def honk: String = "Honk!" } // This will produce a compile error // val carContainer = new PetContainer(new Car)
Upper bounds in Scala enable us to restrict the types we can use with generics, ensuring more type safety and allowing for abstraction over a set of types. This mechanism provides flexibility while still maintaining strong typing guarantees.
Generic types with upper bounds in Scala:
class Container[T <: Comparable[T]](value: T) { def isGreaterThan(other: T): Boolean = value.compareTo(other) > 0 }
Type constraints in Scala with upper bounds:
def findMax[T <: Comparable[T]](values: List[T]): T = values.reduce((x, y) => if (x.compareTo(y) > 0) x else y)
Upper bounds vs lower bounds in Scala:
def processContainer[T >: SubType](container: Container[T]): Unit = { // Code to process containers with a supertype }
Covariance and contravariance with upper bounds:
trait Animal class Container[+T <: Animal](value: T)
Upper bounds in trait and class hierarchies in Scala:
trait Worker class Manager[T <: Worker](val employee: T)
Common patterns for using upper bounds in Scala:
def findMax[T <: Ordered[T]](values: List[T]): T = values.max
Advanced type system features with Scala upper bounds:
trait Container[A] { type Element <: A def extract: Element }