Start Coding

Topics

Scala Generics

Generics in Scala provide a way to write flexible, reusable code that works with different types. They allow you to create classes, traits, and methods that operate on a variety of data types while maintaining type safety.

Basic Syntax

To define a generic class or trait in Scala, use square brackets after the name:

class Box[T](var content: T) {
  def getContent: T = content
  def setContent(newContent: T): Unit = { content = newContent }
}

In this example, T is a type parameter that can be replaced with any concrete type when using the class.

Using Generic Classes

To use a generic class, specify the concrete type when creating an instance:

val intBox = new Box[Int](42)
val stringBox = new Box[String]("Hello, Scala!")

println(intBox.getContent) // Output: 42
println(stringBox.getContent) // Output: Hello, Scala!

Generic Methods

Methods can also be generic, allowing them to work with different types:

def printPair[A, B](a: A, b: B): Unit = {
  println(s"First: $a, Second: $b")
}

printPair(42, "Scala") // Output: First: 42, Second: Scala
printPair("Hello", true) // Output: First: Hello, Second: true

Type Bounds

Scala allows you to specify upper and lower bounds for type parameters:

  • Upper bound: [T <: UpperType] - T must be a subtype of UpperType
  • Lower bound: [T >: LowerType] - T must be a supertype of LowerType
class Animal
class Dog extends Animal
class Puppy extends Dog

def printAnimal[T <: Animal](animal: T): Unit = {
  println(s"Animal: $animal")
}

printAnimal(new Dog()) // Works
printAnimal(new Puppy()) // Works
// printAnimal("Not an animal") // Compilation error

Variance

Scala supports variance annotations for generic types:

  • Covariance: [+T] - If A is a subtype of B, then Box[A] is a subtype of Box[B]
  • Contravariance: [-T] - If A is a subtype of B, then Box[B] is a subtype of Box[A]
  • Invariance: [T] (default) - No subtype relationship between Box[A] and Box[B]

For more details on variance, check out the Scala Variance guide.

Best Practices

  • Use generics to create reusable code that works with multiple types
  • Apply type bounds to restrict the types that can be used with your generic classes or methods
  • Consider variance annotations when designing class hierarchies with generic types
  • Use Scala Type Inference to reduce boilerplate code when working with generics

Generics are a powerful feature in Scala that enables you to write more flexible and maintainable code. They work seamlessly with other Scala features like Pattern Matching and Higher-Order Functions, making them an essential tool in your Scala programming toolkit.