Эта страница документа относится к Scala 3 и может охватывать новые концепции, недоступные в Scala 2. Если не указано явно, все примеры кода на этой странице предполагают, что вы используете Scala 3.
Используемый для типов |
оператор создает так называемый тип объединения (union type).
Тип А | B
представляет значения, которые относятся либо к типу A
, либо к типу B
.
В следующем примере метод help
принимает параметр с именем id
типа объединения Username | Password
,
который может быть либо Username
, либо Password
:
case class Username(name: String)
case class Password(hash: Hash)
def help(id: Username | Password) =
val user = id match
case Username(name) => lookupName(name)
case Password(hash) => lookupPassword(hash)
// дальнейший код ...
Мы реализуем метод help
, разделяя две альтернативы с использованием сопоставления с образцом.
Этот код является гибким и типобезопасным решением.
Если попытаться передать тип, отличный от Username
или Password
, компилятор пометит это как ошибку:
help("hi") // error: Found: ("hi" : String)
// Required: Username | Password
Ошибка также будет получена, если попытаться добавить case
в выражение match
,
которое не соответствует типам Username
или Password
:
case 1.0 => ??? // Ошибка: это строка не компилируется
Альтернатива объединенным типам
Как показано, объединенные типы могут использоваться для представления вариантов нескольких разных типов, не требуя, чтобы эти типы были частью специально созданной иерархии классов.
Предварительное планирование иерархии классов
Другие языки требуют предварительного планирования иерархии классов, как показано в следующем примере:
trait UsernameOrPassword
case class Username(name: String) extends UsernameOrPassword
case class Password(hash: Hash) extends UsernameOrPassword
def help(id: UsernameOrPassword) = ...
Предварительное планирование не очень хорошо масштабируется,
поскольку, например, требования пользователей API могут быть непредсказуемыми.
Кроме того, загромождение иерархии типов маркерами типа UsernameOrPassword
затрудняет чтение кода.
Теговые объединения
Другой альтернативой является задание отдельного типа перечисления, например:
enum UsernameOrPassword:
case IsUsername(u: Username)
case IsPassword(p: Password)
Перечисление UsernameOrPassword
представляет собой помеченное (tagged) объединение Username
и Password
.
Однако этот способ моделирования объединения требует явной упаковки и распаковки,
и, например, Username
не является подтипом UsernameOrPassword
.
Вывод типов объединения
Компилятор присваивает типу объединения выражение, только если такой тип явно задан. Например, рассмотрим такие значения:
val name = Username("Eve") // name: Username = Username(Eve)
val password = Password(123) // password: Password = Password(123)
В этом REPL примере показано,
как можно использовать тип объединения при привязке переменной к результату выражения if
/else
:
scala> val a = if true then name else password
val a: Object = Username(Eve)
scala> val b: Password | Username = if true then name else password
val b: Password | Username = Username(Eve)
Типом a
является Object
, который является супертипом Username
и Password
,
но не наименьшим супертипом, Password | Username
.
Если необходим наименьший супертип, его нужно указать явно, как это делается для b
.
Типы объединения являются двойственными типам пересечения. И как
&
с типами пересечения,|
также коммутативен:A | B
того же типа, что иB | А
.
Contributors to this page:
Contents
- Введение
- Возможности Scala
- Почему Scala 3?
- Почувствуй Scala
- Пример 'Hello, World!'
- REPL
- Переменные и типы данных
- Структуры управления
- Моделирование данных
- Методы
- Функции первого класса
- Одноэлементные объекты
- Коллекции
- Контекстные абстракции
- Верхнеуровневые определения
- Обзор
- Первый взгляд на типы
- Интерполяция строк
- Структуры управления
- Моделирование предметной области
- Инструменты
- Моделирование ООП
- Моделирование ФП
- Методы
- Особенности методов
- Main методы в Scala 3
- Обзор
- Функции
- Анонимные функции
- Параметры функции
- Eta расширение
- Функции высшего порядка
- Собственный map
- Создание метода, возвращающего функцию
- Обзор
- Пакеты и импорт
- Коллекции в Scala
- Типы коллекций
- Методы в коллекциях
- Обзор
- Функциональное программирование
- Что такое функциональное программирование?
- Неизменяемые значения
- Чистые функции
- Функции — это значения
- Функциональная обработка ошибок
- Обзор
- Типы и система типов
- Определение типов
- Параметризованные типы
- Пересечение типов
- Объединение типов
- Алгебраические типы данных
- Вариантность
- Непрозрачные типы
- Структурные типы
- Зависимые типы функций
- Другие типы
- Контекстные абстракции
- Методы расширения
- Параметры контекста
- Контекстные границы
- Given импорты
- Классы типов
- Многостороннее равенство
- Неявное преобразование типов
- Обзор
- Параллелизм
- Scala утилиты
- Сборка и тестирование проектов Scala с помощью Sbt
- Рабочие листы
- Взаимодействие с Java