Классы и конструкторы

Класс в Scala объявляется так коротко, что параметры конструктора умещаются прямо в его имени.

«Меньше шаблонного кода — больше видно сути: класс описывает, что объект знает и умеет.»

Класс — это шаблон для создания объектов. В Scala параметры основного конструктора пишутся сразу после имени класса. Это убирает горы повторяющегося кода, привычного по Java.

class Point(val x: Int, val y: Int):
  def distanceTo(other: Point): Double =
    val dx = x - other.x
    val dy = y - other.y
    math.sqrt(dx * dx + dy * dy)

val p1 = Point(0, 0)
val p2 = Point(3, 4)
println(p1.distanceTo(p2))  // 5.0

Обратите внимание: val x в скобках сразу создаёт неизменяемое поле, доступное снаружи. Если написать var, поле будет изменяемым. Без ключевого слова параметр виден только внутри конструктора.

Создание объектов без new

В Scala 3 можно создавать объекты без слова newPoint(3, 4) вместо new Point(3, 4). Это делает код чище.

Тело класса и инициализация

Любой код в теле класса (вне методов) выполняется при создании объекта — это часть конструктора.

class Account(val owner: String, initial: Int):
  var balance = initial            // изменяемое поле
  println(s"Создан счёт для $owner")  // выполнится при new

  def deposit(amount: Int): Unit =
    balance += amount

val acc = Account("Иван", 100)   // печатает сообщение
acc.deposit(50)
println(acc.balance)             // 150

Та же идея на Python ▶

# Класс на Python — конструктор __init__
import math

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def distance_to(self, other):
        dx = self.x - other.x
        dy = self.y - other.y
        return math.sqrt(dx * dx + dy * dy)

p1 = Point(0, 0)
p2 = Point(3, 4)
print(p1.distance_to(p2))   # 5.0
class Point ( val x: Int , val y: Int )
      ^         \__________________/
     имя        параметры конструктора = поля

Как работает под капотом (JVM)

Scala-класс компилируется в обычный класс JVM. Параметры конструктора с val становятся приватными полями с автоматически сгенерированными методами-геттерами. Код в теле класса перемещается в конструктор JVM (<init>). Когда вы пишете Point(3, 4) без new, компилятор всё равно генерирует вызов конструктора JVM — это просто синтаксический сахар. Поэтому Java-код может использовать ваши Scala-классы как родные.

Частые ошибки

  • Забыть val/var у параметра. Тогда он не станет полем и будет недоступен в методах после конструктора.
  • Лепить var по умолчанию. Предпочитайте val-поля для неизменяемых объектов.
  • Тяжёлая логика в теле класса. Помните: она выполняется при каждом создании объекта.

Best practices

  • Делайте поля val, если объект не должен меняться.
  • Создавайте объекты без new — это идиоматично для Scala 3.
  • Для чистых данных без поведения предпочитайте case class (изучим в разделе про паттерн-матчинг).

Класс в Scala против класса в Java

Если вы знакомы с Java, лаконичность Scala-классов поначалу удивляет. То, что в Java занимает десятки строк — приватные поля, конструктор, геттеры — в Scala умещается в одну строку с параметрами конструктора. Это не косметика: меньше шаблонного кода означает меньше места для ошибок и больше внимания к сути. Вы сразу видите, какие данные несёт объект, не продираясь сквозь церемонии.

При этом важно выбирать правильный инструмент. Обычный class подходит, когда у объекта есть поведение, состояние или идентичность. Но для чистых структур данных — точек, заказов, конфигураций — почти всегда лучше case class, который мы изучим позже: он бесплатно даёт сравнение по значению, удобный вывод и поддержку паттерн-матчинга. Привыкайте спрашивать себя: это объект с поведением или просто пакет данных?

Полезно с самого начала держать в голове разделение ответственности: класс описывает одну сущность и её поведение, не пытаясь делать всё сразу. Если класс разрастается и начинает отвечать за слишком многое, это сигнал разбить его на части или вынести данные в отдельную структуру. Маленькие, сфокусированные классы проще тестировать, переиспользовать и понимать спустя месяцы после написания.

Итоги. Класс объявляется с параметрами конструктора в скобках; val/var делают их полями. Объекты создаются без new. Дальше — объекты-одиночки и companion-объекты.

Проверьте себя
1. Что делает val перед параметром конструктора: class Point(val x: Int)?
AДелает параметр обязательным
BСоздаёт неизменяемое публичное поле x
CЗапрещает создавать объект
DПревращает класс в функцию
2. Как идиоматично создать объект класса Point в Scala 3?
Anew Point(3, 4)
BPoint(3, 4)
CPoint.create(3, 4)
Dmake Point(3, 4)