vals, vars, and Types
Scala gives you two ways to bind a name to a value: val creates an immutable binding that can be assigned exactly once, while var creates a mutable binding that can be reassigned as many times as needed. Scala is also statically typed, meaning every val or var has a fixed type determined at compile time — either written explicitly or inferred automatically from the initializer — and idiomatic Scala code favors val by default, reserving var for the relatively rare cases where genuine mutation is required.
Cricket analogy: Choosing val over var in Scala is like a captain locking in a batting order before the toss rather than shuffling it mid-innings — once declared, a val binding, like Rohit Sharma opening every single match, cannot be swapped out later in the same scope.
Immutability with val
It's important to distinguish reference immutability from object immutability: a val guarantees the binding itself can never point to a different object, but if that object is an inherently mutable data structure — such as a scala.collection.mutable.ArrayBuffer — its internal contents can still change through its own methods. This is different from truly immutable data (like a List or a case class with only vals), which cannot be changed at all once constructed, regardless of how the reference to it is held.
Cricket analogy: A val referencing a mutable ArrayBuffer is like a fixed playing XI announced for the match (the val — you can't swap the list itself) even though individual players' batting stats (the buffer's contents) keep updating ball by ball during the innings.
Mutability with var
var is best reserved for narrowly scoped, genuinely mutable state — a loop counter, a running accumulator inside a tight block — rather than being sprinkled throughout a codebase. Widespread use of var makes programs harder to reason about, because the value at any point depends on execution history rather than being a fixed fact, and it also introduces real risk under concurrency, since two threads reassigning the same var without synchronization can produce race conditions.
Cricket analogy: Using var for a running total, like a var runs = 0 updated after every ball, is like a scoreboard operator manually updating the total after each delivery — necessary and legitimate, but if every single statistic on the ground were mutable and shared, chaos and scoring errors would follow, which is why Scala encourages minimizing var usage.
Type Inference and Explicit Typing
Scala's type inference lets you write val count = 10 and have the compiler deduce Int without any annotation, similarly inferring Double from 19.99, Boolean from true, and String from a quoted literal. Inference is a convenience, not a loosening of the type system — the inferred type is checked at compile time exactly as if you had written it — and best practice is to still write explicit type annotations on public method signatures so the contract is documented and doesn't silently change if the implementation is refactored.
Cricket analogy: Scala's type inference determining val runs = 42 is an Int without you writing Int is like an experienced umpire instantly recognizing a delivery as a no-ball from the bowler's front foot alone, without needing it spelled out — but for a public API, explicitly stating the type is like formally declaring a review decision on the big screen so everyone, including future teammates reading your code, is unambiguously clear.
// Immutable binding — cannot be reassigned
val language: String = "Scala"
// language = "Java" // Compile error: reassignment to val
// Mutable binding — can be reassigned
var attempts: Int = 0
attempts = attempts + 1 // OK
attempts += 1 // OK, same effect
// Type inference: the compiler infers Int, Double, Boolean, String
val count = 10 // Int
val price = 19.99 // Double
val isActive = true // Boolean
val name = "Ada Lovelace" // String
// Explicit typing, recommended for public API signatures
def computeArea(radius: Double): Double = math.Pi * radius * radius
// val referencing a mutable collection: the reference is fixed,
// but the collection's contents can still change
import scala.collection.mutable.ArrayBuffer
val scores: ArrayBuffer[Int] = ArrayBuffer(10, 20, 30)
scores += 40 // legal: mutates the buffer's contents, not the val binding
Scala's type inference means you rarely need to write val count: Int = 10 — the compiler determines Int from the literal on the right-hand side. This removes a lot of the boilerplate common in older Java code while keeping the full safety of a statically typed language, since the inferred type is checked at compile time just as if you'd written it explicitly.
Attempting to reassign a val (e.g. language = "Kotlin" after val language = "Scala") is a compile-time error, not a runtime one — this is a deliberate safety feature. Also be wary of overusing var: idiomatic Scala favors immutability by default, and code with many mutable variables is harder to reason about and more prone to concurrency bugs.
valcreates an immutable binding — it can be assigned once and never reassigned.varcreates a mutable binding that can be reassigned as many times as needed.- A
valbound to a mutable object (like an ArrayBuffer) still allows the object's internal contents to change; only the reference is fixed. - Scala infers types automatically from initializer expressions, but explicit type annotations are recommended on public method signatures.
- Common built-in types include Int, Double, Boolean, String, and Char.
- Idiomatic Scala favors
valovervarto make code easier to reason about and safer under concurrency. - Reassigning a val is a compile-time error, catching mistakes before the program ever runs.
Practice what you learned
1. What happens if you try to reassign a val after it's been initialized?
2. Which keyword creates a mutable binding in Scala?
3. If `val buf = ArrayBuffer(1,2,3)` and you call `buf += 4`, what happens?
4. What does Scala's type inference do?
5. Which is best practice for a public method's return type in idiomatic Scala?
Was this page helpful?
You May Also Like
What Is Scala?
An introduction to Scala as a statically typed, JVM-based language that fuses object-oriented and functional programming paradigms.
Scala Operators and Expressions
How Scala treats operators as method calls, covers arithmetic, comparison, and logical operators, and why almost everything in Scala is an expression that returns a value.
Your First Scala Program
A hands-on walkthrough of writing, compiling, and running a complete Scala program, from the @main entry point to reading input from the console.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics