Option: безопасная работа с отсутствием
Option — это ответ Scala на «ошибку на миллиард долларов»: вместо коварного null отсутствие значения становится видимым в типе.
«Option честно говорит: значения может не быть. И компилятор не даст вам об этом забыть.»
Во многих языках отсутствие значения обозначают null, что приводит к внезапным падениям. Scala предлагает Option — контейнер, который либо содержит значение (Some(x)), либо пуст (None). Отсутствие становится частью типа, и его нельзя проигнорировать.
val found: Option[Int] = Some(42)
val empty: Option[Int] = None
def findUser(id: Int): Option[String] =
if id == 1 then Some("Аня") else None
println(findUser(1)) // Some(Аня)
println(findUser(99)) // NoneOption[String]
/ \
Some("Аня") None
значение есть значения нет
(но НЕ null — это честный тип)Безопасное извлечение через getOrElse
Чтобы достать значение, не падая на пустоте, используют getOrElse — он даёт запасное значение для None.
val name = findUser(99).getOrElse("Гость")
println(name) // Гость
val name2 = findUser(1).getOrElse("Гость")
println(name2) // Аняmap и flatMap на Option
Главная красота: Option ведёт себя как коллекция из 0 или 1 элемента. map преобразует значение, если оно есть, и ничего не делает для None — проверок на пустоту не нужно.
val len: Option[Int] = findUser(1).map(_.length)
println(len) // Some(3)
val none: Option[Int] = findUser(99).map(_.length)
println(none) // None — map просто пропустилOption в for-выражении
def parseAge(s: String): Option[Int] = s.toIntOption
val result = for
a <- parseAge("25")
b <- parseAge("5")
yield a + b
println(result) // Some(30)
val bad = for
a <- parseAge("25")
b <- parseAge("xx") // None
yield a + b
println(bad) // None — вся цепочка свернулась в NoneТа же идея на Python ▶
# Аналог Option на Python — None плюс явные проверки
def find_user(uid):
return "Аня" if uid == 1 else None # как Some/None
name = find_user(99) or "Гость" # как getOrElse
print(name) # Гость
# map на Option = проверка перед преобразованием
user = find_user(1)
length = len(user) if user is not None else None # как .map(_.length)
print(length) # 3
# цепочка с возможным None
def parse_int(s):
try: return int(s)
except ValueError: return None
a, b = parse_int("25"), parse_int("xx")
result = a + b if a is not None and b is not None else None
print(result) # NoneКак работает под капотом (JVM)
Option — это sealed-иерархия: Some[A] (case-класс с одним полем) и объект None. То есть это обычные объекты JVM, а не магия. map на Option реализован через паттерн-матчинг: для Some(x) применяет функцию и оборачивает результат в Some, для None возвращает None. Поскольку Option имеет map и flatMap, он работает в for-выражениях — компилятор переписывает их так же, как для коллекций.
Частые ошибки
- Вызывать
.getбез проверки. НаNoneэто бросит исключение — почти какnull. ПредпочитайтеgetOrElseилиmap. - Городить
if option.isDefined. Часто чище использоватьmap/getOrElseили паттерн-матчинг. - Возвращать
nullиз метода. В Scala 3 для «может не быть значения» используйтеOption.
Best practices
- Возвращайте
Optionвместоnull, когда значение может отсутствовать. - Извлекайте значение через
getOrElse,mapилиmatch, а не через.get. - Соединяйте несколько
Optionвfor-выражении — пустота автоматически прервёт цепочку.
Конец эпохи null
Тони Хоар, изобретатель ссылки null, назвал её своей «ошибкой на миллиард долларов» — за бесчисленные падения программ, которые она вызвала. Option — это структурное решение проблемы. Вместо того чтобы любое значение тайно могло оказаться null, отсутствие выражается явным типом, который виден в сигнатурах. Функция, возвращающая Option[String], честно предупреждает: значения может не быть, обработай это.
Самое элегантное в Option — что он ведёт себя как коллекция из нуля или одного элемента. Это позволяет применять к нему те же map, filter и for, что и к спискам, без единой проверки на пустоту. Цепочка преобразований над Option автоматически останавливается, как только встретит None. Так код, работающий с возможным отсутствием, выглядит так же чисто, как код с гарантированными значениями — а компилятор всё это время следит, что вы не забыли про пустой случай.
Привыкайте читать тип Option в сигнатуре как явное предупреждение: «здесь значения может не быть». Это превращает работу с отсутствием из источника внезапных падений в обычную, контролируемую часть логики. Со временем отсутствие null в Scala-коде начинает ощущаться как глоток свежего воздуха — целый класс ночных дежурств по разбору NullPointerException просто исчезает.
Итоги. Option (Some/None) заменяет null, делая отсутствие явным; getOrElse, map и for работают с ним безопасно. Дальше — обработка ошибок через Try и Either.