Scala - Data Types (Int, Double, String, Boolean, etc.)

• Scala provides a unified type system where everything is an object, including primitive types like Int and Boolean, eliminating the primitive/wrapper distinction found in Java while maintaining...

Key Insights

• Scala provides a unified type system where everything is an object, including primitive types like Int and Boolean, eliminating the primitive/wrapper distinction found in Java while maintaining performance through compiler optimization • Value types in Scala (AnyVal subtypes) are immutable and stack-allocated by default, offering both safety and performance, with automatic conversions between numeric types following specific widening rules • String interpolation and the Nothing/Null types form critical parts of Scala’s type system, enabling type-safe string formatting and proper handling of absence and bottom types in the type hierarchy

The Unified Type System

Scala treats all data types as objects in a unified type hierarchy. At the root sits Any, which branches into AnyVal (value types) and AnyRef (reference types). This eliminates Java’s primitive/object dichotomy while the compiler optimizes AnyVal types to JVM primitives for performance.

val x: Any = 42
val y: Any = "hello"
val z: Any = true

// All values have methods
println(42.toString)
println(42.+(10))  // Operators are methods
println(42 max 50)

The type hierarchy ensures type safety while maintaining flexibility. Every type ultimately extends Any, providing universal methods like toString, equals, and hashCode.

Numeric Types

Scala provides eight numeric value types with specific bit widths and ranges:

val byte: Byte = 127                    // 8-bit signed (-128 to 127)
val short: Short = 32767                // 16-bit signed
val int: Int = 2147483647              // 32-bit signed
val long: Long = 9223372036854775807L  // 64-bit signed (note L suffix)

val float: Float = 3.14f               // 32-bit IEEE 754 (note f suffix)
val double: Double = 3.14159265359     // 64-bit IEEE 754

// Hexadecimal and binary literals
val hex: Int = 0xFF
val binary: Int = 0b1010

// Underscores for readability
val million: Int = 1_000_000

Numeric types support automatic widening conversions but require explicit narrowing:

val i: Int = 42
val l: Long = i        // Implicit widening: Int to Long
val d: Double = i      // Implicit widening: Int to Double

val x: Long = 100L
// val y: Int = x      // Compilation error
val y: Int = x.toInt   // Explicit narrowing required

// Conversion methods
val str = "123"
val num = str.toInt
val dbl = str.toDouble

Boolean Type

Boolean represents logical values with two instances: true and false. Scala uses short-circuit evaluation for logical operators:

val isValid: Boolean = true
val isComplete: Boolean = false

// Logical operators
val and = isValid && isComplete        // false
val or = isValid || isComplete         // true
val not = !isValid                     // false

// Short-circuit evaluation
def expensiveCheck(): Boolean = {
  println("Expensive operation")
  true
}

val result = false && expensiveCheck() // Doesn't print
val result2 = true || expensiveCheck() // Doesn't print

Comparison operators return Boolean values:

val a = 10
val b = 20

println(a == b)   // false
println(a != b)   // true
println(a < b)    // true
println(a <= b)   // true

Char Type

Char represents 16-bit Unicode characters, enclosed in single quotes:

val letter: Char = 'A'
val digit: Char = '9'
val unicode: Char = '\u0041'  // 'A' in Unicode

// Special escape sequences
val newline: Char = '\n'
val tab: Char = '\t'
val backslash: Char = '\\'
val quote: Char = '\''

// Char arithmetic
val nextLetter = (letter + 1).toChar  // 'B'
val charCode: Int = letter.toInt      // 65

// Character methods
println(letter.isUpper)     // true
println(digit.isDigit)      // true
println(letter.toLower)     // 'a'

String Type

Strings are immutable sequences of characters. Scala provides multiple ways to create and manipulate strings:

val simple: String = "Hello, World"
val multiline: String = """This is a
                          |multiline string
                          |with preserved formatting""".stripMargin

// String concatenation
val greeting = "Hello" + " " + "Scala"

// String methods (from Java)
println(simple.length)              // 12
println(simple.toUpperCase)         // "HELLO, WORLD"
println(simple.substring(0, 5))     // "Hello"
println(simple.contains("World"))   // true
println(simple.split(","))          // Array("Hello", " World")

String Interpolation

Scala’s string interpolation provides type-safe string formatting:

val name = "Alice"
val age = 30
val height = 1.65

// s-interpolator: basic string interpolation
val intro = s"My name is $name and I'm $age years old"
val calc = s"Next year I'll be ${age + 1}"

// f-interpolator: formatted strings
val formatted = f"$name is $height%.2f meters tall"
val padded = f"$age%05d"  // "00030"

// raw-interpolator: no escape processing
val path = raw"C:\Users\$name\Documents"  // Backslashes preserved

// Custom interpolators
implicit class JsonHelper(val sc: StringContext) extends AnyVal {
  def json(args: Any*): String = {
    val strings = sc.parts.iterator
    val expressions = args.iterator
    val buf = new StringBuilder(strings.next())
    while(strings.hasNext) {
      buf.append("\"" + expressions.next() + "\"")
      buf.append(strings.next())
    }
    buf.toString
  }
}

val key = "username"
val value = "alice"
val jsonStr = json"""{"$key": $value}"""  // {"username": "alice"}

Unit Type

Unit represents the absence of a meaningful value, similar to void in Java. It has exactly one instance: ().

def printMessage(msg: String): Unit = {
  println(msg)
  // Implicit return of ()
}

val result: Unit = printMessage("Hello")
println(result)  // ()

// Unit is useful for side effects
def updateDatabase(data: String): Unit = {
  // Perform side effect
  println(s"Updating database with: $data")
}

Nothing and Null Types

Nothing is a bottom type, a subtype of all types. It has no instances and represents computations that never complete normally:

def error(message: String): Nothing = {
  throw new RuntimeException(message)
}

def divide(a: Int, b: Int): Int = {
  if (b == 0) error("Division by zero")
  else a / b
}

// Nothing can be used where any type is expected
val list: List[Int] = List(1, 2, 3)
val empty: List[Nothing] = List()
val combined: List[Int] = list ++ empty  // Works because Nothing <: Int

Null is a subtype of all reference types with a single instance: null. Avoid using null in Scala; use Option instead:

// Avoid this
var name: String = null

// Prefer this
val name: Option[String] = None
val name2: Option[String] = Some("Alice")

name2 match {
  case Some(n) => println(s"Name is $n")
  case None => println("No name provided")
}

// Option methods
println(name2.getOrElse("Unknown"))     // "Alice"
println(name2.map(_.toUpperCase))       // Some("ALICE")

Type Inference and Explicit Typing

Scala’s type inference reduces verbosity while maintaining type safety:

// Type inference
val x = 42                    // Inferred as Int
val y = 3.14                  // Inferred as Double
val z = "hello"               // Inferred as String
val flag = true               // Inferred as Boolean

// Explicit typing for clarity
val pi: Double = 3.14159
val count: Long = 1000000L

// Type ascription
val result = (42: Double)     // Treats 42 as Double
val list = List(1, 2, 3): Seq[Int]  // Widens to Seq

// When inference fails or needs help
val emptyList = List.empty[Int]
val emptyMap = Map.empty[String, Int]

Understanding Scala’s type system enables you to write safer, more expressive code. The unified type hierarchy, combined with type inference and pattern matching, creates a powerful foundation for functional and object-oriented programming patterns.

Liked this? There's more.

Every week: one practical technique, explained simply, with code you can use immediately.