Anatomy of a Scala file
Scala is a statically typed, multi-paradigm language that combines object-oriented and functional programming, most commonly targeting the JVM.
File extensions: .scala
Every part of the example below is labelled and explained. This page is one of 55 annotated tours on AnatomyOf, a free, open-source project by LunarWerx Studios.
What is inside a Scala file
Package declaration
Defines the namespace a file's contents belong to.
A package declaration at the top of a file places every top-level definition below it into that namespace, mirroring the directory structure convention inherited from Java (com.example.scala typically lives under com/example/scala/). Unlike Java, a single file can declare multiple nested packages with curly-brace syntax, though the one-package-per-file style shown here is far more common. Scala also supports package objects (package object scala { ... }) for holding values and functions that do not belong inside any single class, giving a package its own top-level members the way a module woul
Import statement
Brings classes or packages into scope.
import java.time.LocalDate brings a single class into scope, but Scala imports are more flexible than Java's: import java.time._ wildcard-imports everything in a package, import java.time.{LocalDate, LocalTime} selects several names at once, and import java.time.{LocalDate => LD} renames a name on the way in to dodge a collision. Imports can also appear anywhere a statement can, not just at the top of the file: scoping an import to a single method or block keeps its effect local, which is handy when two libraries export a colliding name.
Comment
Single-line (//) or block (/* */), ignored by the compiler.
Scala inherits C-family comment syntax: // runs to the end of the line, and /* ... */ spans multiple lines and can nest, which is a small but genuinely useful departure from Java and C, where nesting block comments is illegal. Comments have zero effect on the compiled bytecode. Scaladoc comments (/** ... */) placed directly above a definition are extracted into HTML API documentation, similar to Javadoc, and support tags like @param and @return plus inline links to other symbols.
Object definition (singleton)
Defines a singleton object. Exactly one instance ever exists.
An object declares a class and its sole instance in one step; the runtime lazily creates that instance the first time it is referenced. This is how Scala gets rid of Java's static keyword entirely. Anything you would have made static in Java becomes a member of an object instead. A common pattern pairs an object with a class of the same name (a "companion object"), which gets privileged access to the class's private members and is the idiomatic home for factory methods and constants related to that type.
Variable declaration
val (immutable) or var (mutable).
val binds a name to a value permanently: closer to a Java final variable than a constant, since the value itself can still be a mutable object. var allows reassignment, but idiomatic Scala reaches for val by default and treats var as the exception that needs a reason. Type annotations (val version: String = "1.0.0") are usually optional thanks to type inference, but are common on public members and constructor parameters to keep the inferred API stable as an implementation changes.
Class definition
Blueprint for objects; constructor params can be fields (val/var).
Scala folds the constructor directly into the class signature: class Person(val name: String, var age: Int) declares two fields and a primary constructor in one line, with no separate constructor method or manual field assignment needed. Prefixing a parameter with val or var (or nothing, for a private, unexposed parameter) controls whether it becomes a public field at all. Classes can extend at most one other class but mix in any number of traits (class Dog extends Animal with Loud with Trainable), which is Scala's answer to multiple inheritance without the diamond-inheritance ambiguity of tru
Method definition (def)
Defines a reusable block of code.
The def keyword introduces a method; its return type can usually be inferred, though recursive methods require an explicit annotation. A method body that is a single expression can skip the braces entirely: def greet(): String = s"Hello, $name" is complete without a return statement, since the last expression evaluated is the value produced. Methods can take multiple parameter lists (def add(a: Int)(b: Int): Int), which enables partial application and is the mechanism behind Scala's curried functions and DSL-style syntax.
String interpolation
Embeds expressions in strings using $ or ${}.
An s"..." string lets $name splice in a simple identifier, while ${expression} handles anything more complex, including method calls and field access like ${AppConfig.version}. The s prefix is itself just a method call desugared by the compiler, Scala also ships f"..." for printf-style formatting and raw"..." for strings that skip escape processing. Because interpolators are ordinary macros, libraries define custom ones too; the same $/${} splicing syntax can back things like SQL query builders or JSON literals with compile-time checking of the interpolated types.
Main method
An explicit main method provides a cross-version executable entry point.
def main(args: Array[String]): Unit inside a singleton object uses the conventional JVM entry signature and works across Scala 2 and Scala 3. The runtime calls it with the command-line arguments after loading the object. Scala 3 also offers the concise @main annotation. The older extends App style has only limited support in Scala 3, no longer provides its previous delayed-initialization behavior, and is not the recommended cross-version entry pattern.
Object instantiation
Creating a new instance using new.
new Person("Alice", 30) allocates a new instance and runs the primary constructor with the given arguments, exactly as new does in Java. Scala keeps new mandatory for ordinary classes, though case classes (declared with case class) get a compiler-generated companion factory so they can be constructed without it, e.g. Person("Alice", 30). Case classes also get free structural equality, hashCode, toString, and a copy method out of the box, which is why they, rather than new-instantiated plain classes, are the idiomatic choice for simple data-holding types in Scala.
Control flow (expression & loop)
if is an expression; for loops or comprehends.
Because if/else is an expression rather than a statement, val status = if (alice.age > 25) "Adult" else "Young" needs no ternary operator. The branch that runs supplies the value directly, and both branches must produce compatible types. for (i <- 1 to 3) { ... } iterates a Range, with <- reading as "drawn from." The same for syntax is sugar for chained map/flatMap/filter calls, so a for loop with a yield clause becomes a comprehension that builds a new collection rather than just looping for side effects: a distinction worth knowing since the two look nearly identical.
Official Scala site · All languages on AnatomyOf
The interactive tour needs JavaScript. Enable it to hover a callout and trace it into the code.