Scala 3 Module 1 – Start here: Scala 3.9 LTS on the JVM
Scala 3 for Java and Python developers
Map Java classes, records, static members, switch and Optional, and Python data classes, comprehensions and None, to Scala 3, and avoid five early traps.
What you will learn
- Map Java classes, records, static members, switch, Optional, streams and checked exceptions to Scala
- Map Python data classes, comprehensions, None and duck typing to Scala
- Rewrite a for-comprehension as the withFilter, map and flatMap calls it stands for
- Avoid five first-week traps, from == against eq and Unit to nulls from Java, immutable lists and Int overflow
Before you start
What you’ll learn · Before you start
What you will learn
- Map Java classes, records, static members, switch, Optional, streams and checked exceptions to Scala
- Map Python data classes, comprehensions, None and duck typing to Scala
- Rewrite a for-comprehension as the withFilter, map and flatMap calls it stands for
- Avoid five first-week traps, from == against eq and Unit to nulls from Java, immutable lists and Int overflow
Before you start
On this page
The same order total takes 53 lines of Java, 33 of Scala and 47 of Python in this lesson, and all three programs
print the same eight lines. Most of what you know carries over, because classes, lists, maps and if exist in Scala
too. What changes is where things live and what counts as a value: there is no static, a match returns a value,
and a value that may be missing is an Option rather than null or None.
This lesson maps Java and Python to Scala side by side, shows what a for-comprehension turns into, and walks through five traps that catch experienced developers in their first week of Scala.
One order total in three languages
Here is the program three times: an order of three lines, a discount code that may be unknown, and shipping that depends on the method. Switch between the tabs; the Python version ran on Python 3.14.8, and its output is in its own tab:
Java · invoice-java/InvoiceTotals.java
import java.util.List;
import java.util.Optional;
// An order's totals in cents: line totals, a discount code that may be unknown, and shipping by method.
public class InvoiceTotals {
record Line(String item, int quantity, long unitCents) {
long totalCents() {
return quantity * unitCents;
}
}
enum Shipping { STANDARD, EXPRESS, PICKUP }
static long shippingCents(Shipping method) {
return switch (method) {
case STANDARD -> 499;
case EXPRESS -> 1299;
case PICKUP -> 0;
};
}
static Optional<Integer> discountPercent(String code) {
return switch (code) {
case "WELCOME10" -> Optional.of(10);
case "BULK5" -> Optional.of(5);
default -> Optional.empty();
};
}
static String money(long cents) {
return String.format("%d.%02d", cents / 100, cents % 100);
}
public static void main(String[] args) {
List<Line> lines = List.of(
new Line("Notebook", 3, 450),
new Line("Pen", 10, 120),
new Line("Stapler", 1, 1599));
for (Line line : lines) {
System.out.printf("%-9s %3d x %6s = %7s%n", line.item(), line.quantity(), money(line.unitCents()), money(line.totalCents()));
}
long subtotal = lines.stream().mapToLong(Line::totalCents).sum();
String code = "WELCOME10";
int percent = discountPercent(code).orElse(0);
long discount = subtotal * percent / 100;
long shipping = shippingCents(Shipping.EXPRESS);
System.out.printf("%-24s %7s%n", "Subtotal", money(subtotal));
System.out.printf("%-24s %7s%n", "Discount " + code + " (" + percent + "%)", "-" + money(discount));
System.out.printf("%-24s %7s%n", "Shipping, express", money(shipping));
System.out.printf("%-24s %7s%n", "Total", money(subtotal - discount + shipping));
System.out.println("Code SPRING: " + discountPercent("SPRING").map(p -> p + "%").orElse("no discount"));
}
} Scala · invoice-scala/InvoiceTotals.scala
// The same order in Scala 3: a case class for a line, an enum, match expressions and an Option.
case class Line(item: String, quantity: Int, unitCents: Long):
def totalCents: Long = quantity * unitCents
enum Shipping:
case Standard, Express, Pickup
def shippingCents(method: Shipping): Long = method match
case Shipping.Standard => 499
case Shipping.Express => 1299
case Shipping.Pickup => 0
def discountPercent(code: String): Option[Int] = code match
case "WELCOME10" => Some(10)
case "BULK5" => Some(5)
case _ => None
def money(cents: Long): String = f"${cents / 100}%d.${cents % 100}%02d"
@main def invoiceTotals(): Unit =
val lines = List(Line("Notebook", 3, 450), Line("Pen", 10, 120), Line("Stapler", 1, 1599))
for line <- lines do
println(f"${line.item}%-9s ${line.quantity}%3d x ${money(line.unitCents)}%6s = ${money(line.totalCents)}%7s")
val subtotal = lines.map(_.totalCents).sum
val code = "WELCOME10"
val percent = discountPercent(code).getOrElse(0)
val discount = subtotal * percent / 100
val shipping = shippingCents(Shipping.Express)
println(f"${"Subtotal"}%-24s ${money(subtotal)}%7s")
println(f"${s"Discount $code ($percent%)"}%-24s ${"-" + money(discount)}%7s")
println(f"${"Shipping, express"}%-24s ${money(shipping)}%7s")
println(f"${"Total"}%-24s ${money(subtotal - discount + shipping)}%7s")
println("Code SPRING: " + discountPercent("SPRING").map(p => s"$p%").getOrElse("no discount")) Python · invoice_totals.py
# The same order in Python: a frozen dataclass, a generator expression, a match statement and None.
from dataclasses import dataclass
@dataclass(frozen=True)
class Line:
item: str
quantity: int
unit_cents: int
def total_cents(self) -> int:
return self.quantity * self.unit_cents
def shipping_cents(method: str) -> int:
match method:
case "STANDARD":
return 499
case "EXPRESS":
return 1299
case "PICKUP":
return 0
raise ValueError(method)
def discount_percent(code: str) -> int | None:
return {"WELCOME10": 10, "BULK5": 5}.get(code)
def money(cents: int) -> str:
return f"{cents // 100}.{cents % 100:02d}"
lines = [Line("Notebook", 3, 450), Line("Pen", 10, 120), Line("Stapler", 1, 1599)]
for line in lines:
print(f"{line.item:<9} {line.quantity:3d} x {money(line.unit_cents):>6} = {money(line.total_cents()):>7}")
subtotal = sum(line.total_cents() for line in lines)
code = "WELCOME10"
percent = discount_percent(code) or 0
discount = subtotal * percent // 100
shipping = shipping_cents("EXPRESS")
print(f"{'Subtotal':<24} {money(subtotal):>7}")
print(f"{f'Discount {code} ({percent}%)':<24} {'-' + money(discount):>7}")
print(f"{'Shipping, express':<24} {money(shipping):>7}")
print(f"{'Total':<24} {money(subtotal - discount + shipping):>7}")
found = discount_percent("SPRING")
print("Code SPRING: " + (f"{found}%" if found is not None else "no discount")) Output
Notebook 3 x 4.50 = 13.50 Pen 10 x 1.20 = 12.00 Stapler 1 x 15.99 = 15.99 Subtotal 41.49 Discount WELCOME10 (10%) -4.14 Shipping, express 12.99 Total 50.34 Code SPRING: no discount
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 invoice_totals.py
The Java and Scala versions ran on the same JDK 25. Java’s launcher compiles a single source file in memory and runs
it, so java InvoiceTotals.java needs no separate javac step:
from session import run
# The Java version with the JDK's single-file launcher, then the Scala version, on the same JDK 25.
run("""
cd invoice-java
java InvoiceTotals.java
cd ../invoice-scala
scala run . --server=false
""") Output
$ cd invoice-java $ java InvoiceTotals.java Notebook 3 x 4.50 = 13.50 Pen 10 x 1.20 = 12.00 Stapler 1 x 15.99 = 15.99 Subtotal 41.49 Discount WELCOME10 (10%) -4.14 Shipping, express 12.99 Total 50.34 Code SPRING: no discount $ cd ../invoice-scala $ scala run . --server=false Notebook 3 x 4.50 = 13.50 Pen 10 x 1.20 = 12.00 Stapler 1 x 15.99 = 15.99 Subtotal 41.49 Discount WELCOME10 (10%) -4.14 Shipping, express 12.99 Total 50.34 Code SPRING: no discount
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 invoice_session.py
The three outputs are the same, line for line. Put the versions side by side and the pieces match one to one: a Java
record, a Scala case class: A class for immutable data. The compiler adds equals and hashCode that compare the fields, a readable toString, a copy method and support for pattern matching. and a frozen Python data class; an enum of shipping methods
in Java and in Scala, where Python uses plain strings; three ways of choosing, switch, match and Python’s match
statement; and a discount that may be missing, an Optional, an Option: A container that holds either one value, Some(value), or nothing, None. Scala code uses it instead of null for a value that may be missing. and None.
The next two sections go through the differences that matter.
Checkpoint
Check: One program, three languages
From Java: what replaces static, records, switch and checked exceptions
Most Java constructs have a direct Scala counterpart. The table lists the ones every Java developer meets in the first week:
| In Java | In Scala | What changes |
|---|---|---|
| a class with fields and a constructor | class Line(item: String, quantity: Int) |
the parameter list after the name is the constructor |
record Line(...) |
case class Line(...) |
equals, hashCode, toString, copy and pattern matching come with it |
static members |
members of an object: A singleton: a class with exactly one instance, created when it is first used. The members of an object play the role of Java's static members., often the class’s companion object | there is no static keyword |
switch |
match |
an expression with a value; on an enum, a missing case is warning E029 |
Optional<T> |
Option[T]: Some(x) or None |
used across the standard library, and Option(x) turns a Java null into None |
| streams | methods of the collections: map, filter, sum |
each call builds its result at once; no stream() or collect() |
checked exceptions, throws |
no checked exceptions | @throws tells Java callers what a Scala method can throw |
void |
Unit: The type of expressions that run for their effect, such as println. Its only value is written (), and it plays the role of void in Java. | a real type whose only value is () |
final local variable |
val |
var is the variable you can reassign |
Two rows deserve a second look. A Java switch expression over an enum must cover every value, and javac refuses
one that does not, while a Scala match only warns, as the previous lesson showed with E029 and a MatchError. Here
is Java’s side of it, on the same JDK 25:
// A Java switch expression over an enum, with one case missing.
public class Fee {
enum Payment { CARD, WALLET, CASH }
static long feeCents(Payment p) {
return switch (p) {
case CARD -> 30;
case WALLET -> 10;
};
}
public static void main(String[] args) {
System.out.println(feeCents(Payment.CASH));
}
} from session import run
# Java refuses a switch expression that misses a value of its enum; it reports the problem before anything runs.
run("""
cd switch-java
!java Fee.java
""") Output
$ cd switch-java
$ java Fee.java
Fee.java:6: error: the switch expression does not cover all possible input values
return switch (p) {
^
1 error
error: compilation failed
[exit status 1]
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 switch_session.py
The other is checked exceptions. Scala does not check them, so a Scala method never declares what it throws, and Java
code that calls it is not forced to catch anything. When Java callers should be told, annotate the method with
@throws(classOf[java.io.IOException]); Java then treats it as a method that declares throws IOException.
Keep -Werror on for Java's habit
Java developers rely on the compiler to refuse a switch expression with a missing case. Compile Scala with
-Werror, as the previous lesson showed, and a missing case in a match on an enum fails the build in the same
way.
Checkpoint
Check: Java constructs in Scala
From Python: data classes, comprehensions, None and duck typing
Python developers find the indentation familiar: Scala 3 can drop braces and marks blocks by indentation, with then
and do after conditions. The differences are in the types, which Scala checks before the program runs:
| In Python | In Scala | What changes |
|---|---|---|
@dataclass(frozen=True) |
case class |
the Scala 3 Book calls case classes similar to frozen data classes; copy plays the part of replace |
[f(x) for x in xs if p(x)] |
for x <- xs if p(x) yield f(x) |
the result keeps the collection’s type, and the comprehension is a chain of method calls |
None, which any name may hold |
Option[A]: Some(a) or None |
the type says that a value may be missing, and the compiler makes you handle it |
| duck typing | traits, checked by the compiler | a missing method is a compile error rather than an AttributeError at run time |
int, which never overflows |
Int (32 bits), Long (64 bits), BigInt |
past its limit an Int wraps around, so money is counted in Long |
a list you append to |
an immutable List; ArrayBuffer when you must mutate |
:+ returns a new list and leaves the old one unchanged |
The discount_percent function of the Python version shows the biggest change in practice. In Python it returns an
int or None, and nothing stops a caller from adding the None to a number. In Scala it returns Option[Int], and
discountPercent(code) + 1 does not compile: the caller must say what happens when there is no discount, with
getOrElse(0) as the order total does, or with a match.
Key idea
The Scala version of a Java or Python program has the same parts, but its types carry more: a value that may be
missing is an Option, a choice is a match with a value, and shared members live in an object.
Checkpoint
Check: Python constructs in Scala
A for-comprehension is withFilter, map and flatMap
A for-comprehension: A for expression with yield. The compiler rewrites it into map, flatMap and withFilter calls, so it works for lists, Option, Either and any other type with those methods. looks like a Python comprehension, but it is shorthand for
method calls. The language specification gives the rules: a generator with yield becomes map, a second generator
makes the first one a flatMap, and a guard if becomes withFilter. The program prints each comprehension next to
the calls it stands for:
// A for-comprehension is rewritten into withFilter, map and flatMap, so it works on any type that has them.
@main def fors(): Unit =
val marks = List(72, 35, 91, 48)
val doubled = for m <- marks if m >= 40 yield m * 2
val same = marks.withFilter(m => m >= 40).map(m => m * 2)
println(s"for/yield: $doubled")
println(s"rewritten: $same")
val sizes = List("S", "M")
val colours = List("red", "blue")
val pairs = for s <- sizes; c <- colours yield s"$s-$c"
val pairs2 = sizes.flatMap(s => colours.map(c => s"$s-$c"))
println(s"two generators: $pairs")
println(s"rewritten: $pairs2")
def price(code: String): Option[Int] = Map("A1" -> 450, "B2" -> 120).get(code)
println(s"both found: ${for x <- price("A1"); y <- price("B2") yield x + y}")
println(s"one missing: ${for x <- price("A1"); y <- price("Z9") yield x + y}") from session import run
# Each for-comprehension next to the calls the compiler rewrites it into.
run("""
cd fors
scala run . --server=false
""") Output
$ cd fors $ scala run . --server=false for/yield: List(144, 182, 96) rewritten: List(144, 182, 96) two generators: List(S-red, S-blue, M-red, M-blue) rewritten: List(S-red, S-blue, M-red, M-blue) both found: Some(570) one missing: None
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 fors_session.py
Each pair printed the same result. Step through the second one, two generators, as the compiler rewrites it:
The drawing shows the whole scene; its 5 steps are listed below.
Every step as text
- The comprehension. The program writes for s <- sizes; c <- colours yield s"$s-$c": every size with every colour.
- The first generator. Because another generator follows it, the first one becomes a flatMap over sizes. For each size it expects a whole list back, and flatMap joins those lists.
- The last generator. The last generator becomes a map over colours, nested inside the flatMap, so it runs once for every size.
- The yield. What follows yield becomes the body of the innermost function: s"$s-$c" for the current size and colour.
- The result. map gives two strings for each size, and flatMap joins them into one list, List(S-red, S-blue, M-red, M-blue), the same as the session printed for both forms.
Because a for-comprehension is only calls, it works on every type that has map, flatMap and withFilter, not just
on collections. The last two lines use it on Option: when both prices exist, the result is Some(570); when one is
missing, the whole result is None, with no null check anywhere. The same shape works for Either and Future,
which later lessons of this track use.
Checkpoint
Check: What a for-comprehension becomes
Five traps of the first week
Each of these traps compiles without a complaint and then prints something a Java or Python developer does not expect. The program shows all five:
// Five things that surprise Java and Python developers in their first week of Scala.
case class Money(cents: Long)
def log(message: String): Unit = println(s"log: $message")
@main def traps(): Unit =
// 1. == compares values (it calls equals, and handles null); eq compares references.
val a = Money(450)
val b = Money(450)
println(s"a == b: ${a == b}, a eq b: ${a eq b}")
val missing: Money = null
println(s"missing == a: ${missing == a}")
// 2. Unit is a real value, the only one of its type, written ().
val result: Unit = log("saved")
println(s"result: $result")
// 3. A Java method may return null; Option(...) turns null into None.
val home = System.getProperty("app.home")
println(s"raw: $home, as an Option: ${Option(home)}")
println(s"with a default: ${Option(home).getOrElse("/opt/app")}")
// 4. List is immutable: :+ returns a new list and leaves the old one as it was.
val prices = List(450, 120)
val more = prices :+ 1599
println(s"prices: $prices, more: $more")
// 5. Int has 32 bits, unlike Python's int: past its maximum it wraps around.
val biggest = Int.MaxValue
println(s"Int.MaxValue + 1 = ${biggest + 1}, as a Long: ${biggest.toLong + 1}") from session import run
# Five first-week traps, each printed next to what it shows.
run("""
cd traps
scala run . --server=false
""") Output
$ cd traps $ scala run . --server=false a == b: true, a eq b: false missing == a: false log: saved result: () raw: null, as an Option: None with a default: /opt/app prices: List(450, 120), more: List(450, 120, 1599) Int.MaxValue + 1 = -2147483648, as a Long: 2147483648
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 traps_session.py
==compares values. In Scalaa == bcallsequals, and it is safe withnull: the API documentation definesx == thatasif (x eq null) that eq null else x.equals(that). So twoMoney(450)values are equal, andmissing == aisfalseinstead of aNullPointerException. reference equality: Two names that point to the same object in memory. In Scala, a eq b tests it, while a == b calls equals and compares values., Java’s==on objects and Python’sis, iseqin Scala, and it wasfalsefor the two separate objects.Unitis a value. A method that returnsUnit, likelog, still returns something:(), the one value ofUnit, asresult: ()shows. Where Java hasvoid, Scala has an ordinary type.- Java can hand you
null.System.getProperty("app.home")returnednullbecause the property is not set.Option(home)turnsnullintoNoneand anything else intoSome, andgetOrElsethen supplies the default. Wrap values from Java where they enter your Scala code. - A
Listnever changes.prices :+ 1599returned a new list with three items and leftpriceswith two. Code written in the Java or Python habit, calling:+and ignoring the result, loses the item. - An
Intoverflows.Int.MaxValue + 1printed-2147483648: anInthas 32 bits and wraps around, while a Pythonintwould just grow. Convert toLongfirst, as the last value shows, or useBigInt.
Comparing values with eq
eq asks whether two names point to the same object, so two equal strings or case class values can give false, as
a eq b did. Compare values with ==, and keep eq for the rare case where identity matters.
Checkpoint
Check: Five first-week traps
Practise: port a Java method to Scala
Here is a small Java method to port: a letter grade for a mark from 0 to 100, or none for a mark outside that range.
Its Scala starter has the same checks as the Java main, and its grade returns None for now:
import java.util.List;
import java.util.Optional;
// The Java original to port: a letter grade for a mark from 0 to 100, or none when the mark is out of range.
public class Grades {
static Optional<String> grade(int mark) {
if (mark < 0 || mark > 100) {
return Optional.empty();
}
return switch (mark / 10) {
case 10, 9 -> Optional.of("A");
case 8 -> Optional.of("B");
case 7 -> Optional.of("C");
case 6 -> Optional.of("D");
default -> Optional.of("F");
};
}
public static void main(String[] args) {
for (int mark : List.of(95, 83, 71, 64, 12, 101)) {
System.out.println(mark + " -> " + grade(mark).orElse("no grade"));
}
}
} // Practise: port grade from Grades.java. Return Some(letter) for a mark from 0 to 100, and None otherwise.
def grade(mark: Int): Option[String] = None
@main def grades(): Unit =
val expected = List(95 -> Some("A"), 83 -> Some("B"), 71 -> Some("C"), 64 -> Some("D"), 12 -> Some("F"), 101 -> None)
val results =
for (mark, want) <- expected yield
val got = grade(mark)
println(s"${if got == want then "ok " else "FAIL"} $mark -> ${got.getOrElse("no grade")}")
got == want
println(s"${results.count(ok => ok)} of ${results.size} checks pass")
if results.contains(false) then sys.exit(1) The session runs the Java original, then the starter, which passes only the one check that expects no grade:
from session import run
# The Java original, then the Scala starter of the exercise, whose grade always returns None.
run("""
cd grades-java
java Grades.java
cd ../grades
!scala run . --server=false
""") Output
$ cd grades-java $ java Grades.java 95 -> A 83 -> B 71 -> C 64 -> D 12 -> F 101 -> no grade $ cd ../grades $ scala run . --server=false FAIL 95 -> no grade FAIL 83 -> no grade FAIL 71 -> no grade FAIL 64 -> no grade FAIL 12 -> no grade ok 101 -> no grade 1 of 6 checks pass [exit status 1]
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 grades_session.py
Copy grades/Grades.scala into an empty folder, write grade so that scala run . reports 6 of 6 checks pass, and
compare your version with the Java one: which lines did the match replace? First hint: start with an if that
returns None outside 0 to 100, then match on mark / 10, where case 10 | 9 => Some("A") covers two values in
one case.
Then test the five ideas of the lesson in a short session of questions, from the Java and Python maps to the five traps; each answer is explained the moment you give it:
Practice that adapts to you
Practise this lesson
14 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Fresher · What is the difference between == and eq in Scala, and how does it compare with Java and Python?
In Scala, == compares values: for objects it calls equals, and it handles null safely, because x == that is
defined as if (x eq null) that eq null else x.equals(that). eq compares references: it is true only when both
names point to the same object. So two case class values with the same fields are == but not eq. Java is the other
way round: == on objects compares references and .equals compares values, which is why string comparisons with ==
are a classic Java bug. Python matches Scala: == compares values and is compares identity. In everyday Scala you
use == almost always; eq is for the rare code where identity itself matters, such as a cache keyed on one
particular object.
Key takeaways
- The same program has the same parts in Java, Scala and Python, and here all three printed the same order total.
- From Java: records become case classes, static members live in an
object,switchbecomes amatchwith a value,OptionalbecomesOption, and Scala has no checked exceptions. - From Python: frozen data classes become case classes, comprehensions become for-comprehensions,
Nonebecomes theNoneof anOptionthat the type declares, and the compiler checks what duck typing leaves to run time. - A for-comprehension is
withFilter,mapandflatMapcalls, so it works onOptionand other types as well as on collections. ==compares values andeqreferences,Unitis a value,Option(...)guards againstnullfrom Java,Listnever changes in place, and anIntwraps around at 2,147,483,647.
Terms in this lesson
- case class
- A class for immutable data. The compiler adds equals and hashCode that compare the fields, a readable toString, a copy method and support for pattern matching.
- for-comprehension
- A for expression with yield. The compiler rewrites it into map, flatMap and withFilter calls, so it works for lists, Option, Either and any other type with those methods.
- object
- A singleton: a class with exactly one instance, created when it is first used. The members of an object play the role of Java's static members.
- Option
- A container that holds either one value, Some(value), or nothing, None. Scala code uses it instead of null for a value that may be missing.
- reference equality
- Two names that point to the same object in memory. In Scala, a eq b tests it, while a == b calls equals and compares values.
- Unit
- The type of expressions that run for their effect, such as println. Its only value is written (), and it plays the role of void in Java.
References
- Scala 3 Book: Scala for Java developers (Scala Center and EPFL (docs.scala-lang.org))
- Scala 3 Book: Scala for Python developers (Scala Center and EPFL (docs.scala-lang.org))
- Scala 3 Book: interacting with Java (Scala Center and EPFL (docs.scala-lang.org))
- Scala 3 Book: control structures (for expressions) (Scala Center and EPFL (docs.scala-lang.org))
- Scala Language Specification 3.4, chapter 6: Expressions (for comprehensions) (The Scala Programming Language (scala-lang.org))
- Scala 3.9.0 API, scala.Any (== and equals) (The Scala Programming Language (scala-lang.org))
- Scala 3.9.0 API, scala.AnyRef (eq) (The Scala Programming Language (scala-lang.org))
- The Java Language Specification, Java SE 25: chapter 15, Expressions (switch expressions) (Oracle)
- The java command (JDK 25 tool reference, launching source-file programs) (Oracle)
- The Python Language Reference: the match statement (Python Software Foundation)
- Python documentation: dataclasses (Python Software Foundation)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress