Scala Tutorial

Basics

Control Statements

OOP Concepts

Parameterized - Type

Exceptions

Scala Annotation

Methods

String

Scala Packages

Scala Trait

Collections

Scala Options

Miscellaneous Topics

Value classes in Scala

In Scala, value classes are a mechanism to avoid allocating runtime objects, which can be particularly useful when you want to create rich domain primitives without incurring a performance cost. This feature is valuable because one of the best practices in domain-driven design is to avoid using primitive types directly but rather to wrap them in domain-specific types. However, creating a new object for every primitive wrapper can impose a runtime overhead. Value classes come to the rescue in such scenarios.

Defining a Value Class:

  1. To define a value class, create a class that extends AnyVal.
  2. The class should have exactly one primary constructor parameter.
  3. The class should not have any other fields.
  4. The class should have no additional val or var fields.
  5. The class can have defs (methods), but it should not have other state.

Example:

Here's an example of a value class:

class Meter(val value: Double) extends AnyVal {
  def +(other: Meter): Meter = new Meter(value + other.value)
}

val m1 = new Meter(5.0)
val m2 = new Meter(3.0)
val m3 = m1 + m2
println(m3.value)  // 8.0

Limitations:

  1. Value classes cannot extend other classes or traits.
  2. They can only extend universal traits (traits that extend Any with only abstract methods).
  3. Value classes cannot have nested classes, traits, or objects.
  4. Value classes can't be null.

Runtime Characteristics:

Under the hood, the Scala compiler tries to use the underlying primitive representation at runtime whenever possible, avoiding unnecessary object creation. In our example above, Meter instances are typically represented as Double values at runtime.

However, in certain situations, the compiler might still need to box the value class into an object, such as:

  1. When a value class is treated as another type.
  2. When doing runtime type tests, like pattern matching.
  3. When instantiating an array of the value class.
  4. When the value class appears as a type argument in a generic class or method.
  5. When calling methods from the Any reference on the value class.

Conclusion:

Value classes in Scala provide a means to create lightweight wrapper types around primitives (or references) without the usual object allocation overhead. They are useful for creating more meaningful domain types, enforcing type safety, and providing domain-specific operations without sacrificing performance.

  1. Benefits of using value classes in Scala:

    • Description: Value classes provide a way to wrap and optimize new types without the runtime overhead of creating a new object. They offer performance benefits and cleaner code for certain scenarios.
    • Code:
      case class Meters(value: Double) extends AnyVal {
        def toFeet: Double = value * 3.28084
      }
      
  2. When to use value classes in Scala:

    • Description: Value classes are suitable when you want to create new types that are mainly used for type safety and abstraction without incurring the cost of object allocation.
    • Code:
      class Temperature(val value: Double) extends AnyVal {
        def toCelsius: Double = (value - 32) * 5 / 9
      }
      
  3. Defining and using value classes in Scala:

    • Description: Value classes are defined using the extends AnyVal syntax. They should have a single parameter in their primary constructor.
    • Code:
      class UserName(val value: String) extends AnyVal
      val user: UserName = new UserName("JohnDoe")
      
  4. Limitations of value classes in Scala:

    • Description: Value classes come with constraints, such as having only one parameter in the primary constructor and being unable to define mutable fields or extend classes.
    • Code:
      // This will not compile due to the limitation on the number of parameters
      class InvalidValueClass(val value1: Int, val value2: Int) extends AnyVal
      
  5. Comparing value classes with case classes in Scala:

    • Description: Case classes provide more features like pattern matching and structural equality, while value classes offer better performance by avoiding object allocation.
    • Code:
      case class Point(x: Double, y: Double)
      class PointValue(val x: Double, val y: Double) extends AnyVal
      
  6. Implicit conversions and value classes in Scala:

    • Description: Implicit conversions allow automatic conversion between value classes and their underlying types, enhancing flexibility and usability.
    • Code:
      implicit def doubleToTemperature(value: Double): Temperature = new Temperature(value)
      val temp: Temperature = 32.0