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, as in many statically-typed languages, you can create classes that are parameterized by one or more types. These are called generic classes. Generics provide a way to make your code more abstract and reusable while maintaining type safety.
Let's start with a simple example of a generic class:
class Box[T](t: T) { def content: T = t }
In this example, Box
is a generic class that can hold any type T
. The [T]
after the class name denotes the type parameter.
You can create instances of this class with any type:
val intBox = new Box[Int](10) val stringBox = new Box[String]("Hello")
A class can also have multiple type parameters:
class Pair[A, B](val first: A, val second: B)
You can restrict the types that can be used with your generic class by using bounds. Here are the types of bounds you can use:
<:
symbol:class Box[T <: AnyVal](t: T)
The above Box
class now can only be parameterized with types that are subtypes of AnyVal
(e.g., Int
, Double
).
>:
symbol:class Box[T >: String](t: T)
In the context of generic types and subtyping, Scala supports variance annotations:
+
): If B
is a subtype of A
, then Box[B]
is a subtype of Box[A]
.class Box[+T]
-
): If B
is a subtype of A
, then Box[A]
is a subtype of Box[B]
.class Box[-T]
Box[A]
and Box[B]
are not related irrespective of the relationship between A
and B
.You can use context bounds if you want to ensure that there exists an implicit value of a certain type:
def sort[T : Ordering](seq: Seq[T]): Seq[T] = { seq.sorted }
This method ensures that there's an implicit Ordering[T]
available when the method is invoked, which is required by the sorted
method.
Generics in Scala allow developers to create type-safe, reusable, and abstract code. With features like bounded type parameters, variance annotations, and context bounds, Scala provides a rich set of tools to make generics expressive and flexible.
Type parameters in Scala generic classes:
class Box[T](value: T) { def getValue: T = value } val intBox = new Box[Int](42) val stringBox = new Box[String]("Hello, Scala!")
Using generic classes for code reusability in Scala:
class Pair[A, B](first: A, second: B) { def getFirst: A = first def getSecond: B = second } val intStringPair = new Pair[Int, String](1, "One") val doubleBooleanPair = new Pair[Double, Boolean](3.14, true)
Covariance and contravariance in Scala generics:
class Container[+T](value: T) // Covariant val stringContainer: Container[String] = new Container("Hello") val anyContainer: Container[Any] = stringContainer
class Processor[-T](processor: T => Unit) // Contravariant val printString: String => Unit = (s: String) => println(s) val printAny: Any => Unit = printString
Bounds in Scala generic classes:
class Box[T <: Number](value: T) // T must be a subtype of Number val intBox = new Box[Int](42)
class Box[T >: String](value: T) // T must be a supertype of String val anyBox = new Box[Any]("Hello")
Wildcard types and generic classes in Scala:
def printBoxValue(box: Box[_ <: Number]): Unit = { println(box.getValue) }
def processBox(box: Box[_ >: String]): Unit = { // Process the box }
Scala generic classes vs. abstract classes:
class Box[T](value: T) val intBox = new Box[Int](42)
abstract class Shape { def area: Double } class Circle(radius: Double) extends Shape { override def area: Double = math.Pi * radius * radius }
Using generic classes with collections in Scala:
val list: List[Int] = List(1, 2, 3, 4, 5) val set: Set[String] = Set("apple", "orange", "banana")
Variance annotations in Scala generic classes:
+
, -
, or no annotation) specify how subtyping relationships are preserved in generic classes.class Container[+T](value: T) val stringContainer: Container[String] = new Container("Hello") val anyContainer: Container[Any] = stringContainer
class Processor[-T](processor: T => Unit) val printString: String => Unit = (s: String) => println(s) val printAny: Any => Unit = printString
Inheritance and generic classes in Scala:
class Box[T](value: T) class NumberBox[T <: Number](value: T) extends Box[T]
Scala generic methods vs. generic classes:
def printElement[A](element: A): Unit = { println(element) }
Parameterized types and type erasure in Scala:
def printType[A](value: A): Unit = { val typeName = value.getClass.getSimpleName println(s"The type of $value is $typeName") }