Справочник по Scala (Handbook)
Практичное и современное руководство по Scala. Объясняем сложные концепции функционального и объектно-ориентированного программирования простым языком: без заумных математических терминов, но с пониманием того, как писать надёжный код.
- 1. Переменные, иммутабельность и типы: `val` против `var`
- 2. Функции и выражения — в Scala всё возвращает значение
- 3. Case-классы и Сопоставление с образцом (Pattern Matching)
- 4. ООП в Scala: Трейты (Traits) и Объекты-компаньоны
- 5. Безопасная работа с данными: `Option`, `Try` вместо `null`
- 6. Коллекции и их суперсилы: `map`, `filter`, `flatMap`
1. Переменные, иммутабельность и типы: `val` против `var`
В Scala фундаментальное правило гласит: **состояние должно быть неизменяемым (immutable) по умолчанию**. Это избавляет от 90% багов при параллельной работе кода.
val— неизменяемая переменная (константа). Создали — и больше изменить нельзя. Используйте её всегда.var— обычная изменяемая переменная. Используется крайне редко и только там, где без этого не обойтись.
Вывод типов (Type Inference)
Scala — строго типизированный язык, но вам не нужно везде писать типы вручную. Компилятор умеет угадывать их сам:
// Компилятор сам понимает, что это String и Int
val siteName = "FeXcharged"
val version = 3
// Но тип можно указать и явно:
val limit: Double = 100.50
// Попытка переопределить val вызовет ошибку еще до запуска:
// siteName = "Other" -- Ошибка компиляции!
2. Функции и выражения — в Scala всё возвращает значение
В Scala почти любой блок кода — это **выражение (expression)**, то есть он всегда вычисляет и возвращает итоговое значение. Даже обычный if/else работает как тернарный оператор.
Вам не обязательно писать ключевое слово return — функция автоматически возвращает результат последней строчки:
// Определение функции: параметры, тип ответа и тело
def getMax(a: Int, b: Int): Int =
if (a > b) a else b
// Присваиваем результат работы условия сразу в переменную
val status = if (version >= 3) "Modern" else "Legacy"
println(status) // "Modern"
3. Case-классы и Сопоставление с образцом (Pattern Matching)
Case-классы — это супер-заряженные структуры для хранения данных. В них автоматически встроен сгенерированный конструктор, готовые методы equals, toString и быстрый доступ к полям.
В связке с Pattern Matching (продвинутый switch на стереоидах) они позволяют читать структуру объектов за пару строк кода:
// Создаем модель данных за 1 строчку
case class User(name: String, role: String)
val currentUser = User("Мадалбеков", "admin")
// Pattern Matching по ролям и содержимому
val accessMessage = currentUser match {
case User(name, "admin") => s"Полный доступ предоставлен, $name"
case User(name, "guest") => s"Ограниченный просмотр, $name"
case _ => "Неизвестный тип пользователя"
}
println(accessMessage) // "Полный доступ предоставлен, Мадалбеков"
4. ООП в Scala: Трейты (Traits) и Объекты-компаньоны
В Scala гибкая система классов. Вместо стандартных интерфейсов используются **Трейты (Traits)**, а вместо статических методов — **Объекты (object)**.
| Конструкция | Что делает | На что похоже |
|---|---|---|
trait |
Задает поведение. Может содержать и абстрактные, и готовые методы. | Интерфейс в Java/C# + миксины |
object |
Единственный экземпляр на всё приложение (Singleton). | Статический класс / Модуль |
| Компаньон | object и class с одинаковым именем в одном файле. |
Класс со статическими методами |
trait Logger {
def log(msg: String): Unit = println(s"[LOG]: $msg")
}
// Объект-одиночка (Singleton), расширяющий трейт
object AppServer extends Logger {
def start(): Unit = log("Сервер запущен на порту 8080")
}
AppServer.start()
5. Безопасная работа с данными: `Option`, `Try` вместо `null`
В правильном коде на Scala вы почти никогда не увидите NullPointerException. Если значение может отсутствовать, его заворачивают в контейнер Option.
Some(value)— если внутри контейнера реально есть значение.None— если значения нет (вместо использования опасного null).
// Функция возвращает контейнер: данные либо есть, либо нет
def findUser(id: Int): Option[String] = {
if (id == 1) Some("Мурас") else None
}
// Безопасный разбор через getOrElse (задаем дефолт на случай None)
val userName = findUser(2).getOrElse("Гость")
println(userName) // "Гость"
6. Коллекции и их суперсилы: `map`, `filter`, `flatMap`
Коллекции в Scala по умолчанию неизменяемые. Любая операция не меняет старый массив, а возвращает новый с нужными преобразованиями.
val numbers = List(1, 2, 3, 4, 5)
// 1. Фильтрация: оставить только четные
val evens = numbers.filter(n => n % 2 == 0)
// 2. Преобразование: умножить каждое число на 10 (короткая запись с _)
val multiplied = numbers.map(_ * 10)
// 3. Цепочка вызовов
val result = numbers
.filter(_ > 2)
.map(_ * 2)
println(result) // List(6, 8, 10)