Unit
получает специальную обработку компилятором при генерации байтового кода, поскольку он аналогичен void
на jvm. Но концептуально, как тип в системе типа scala, кажется, что он также получает особое отношение к самому языку (примеры ниже).
Итак, мой вопрос в том, чтобы прояснить это и понять, какие механизмы используются, и если действительно существует специальное обращение к типу Unit
.
Пример 1:
Для "обычных" scala типов, таких как Seq
, если метод возвращает Seq
, тогда вы должны вернуть Seq
(или более конкретный тип, который расширяет Seq
)
def foo1: Seq[Int] = List(1, 2, 3)
def foo2: Seq[Int] = Vector(1, 2, 3)
def foo3: Seq[Int] = "foo" // Fails
Первые два примера компилируются, потому что List[Int]
и Vector[Int]
являются подтипами Seq[Int]
. Третий не работает, потому что String
не является.
Но если я изменю третий пример на возврат Unit
, но он будет компилироваться и запускаться без проблем, хотя String
не является подтипом Unit
:
def foo3(): Unit = "foo" // Compiles (with a warning)
Я не знаю ни одного другого типа, для которого это исключение будет разрешено в scala. Также компилятор имеет специальные правила для типа Unit
на уровне системы типов или существует какой-то более общий механизм при работе, например. неявное преобразование.
Пример 2:
Я также не понимаю, как подразделение взаимодействует в ситуациях, когда правило отклонения обычно применяется.
Например, мы иногда попадаем в эту ошибку с помощью Future[Unit]
, где мы случайно используем map
вместо flatMap
и создаем Future[Future]
:
def save(customer: Customer): Future[Unit] = ... // Save to database
def foo: Future[Unit] = save(customer1).map(_ => save(customer2))
map
создает Future[Future[Unit]]
, а для компилятора требуется Future[Unit]
. Но это компилируется!
Сначала я думал, что это потому, что Future[+T]
является ковариантным, но на самом деле Future[Unit]
не является подтипом Unit
, поэтому, похоже, это не так.
Если тип изменяется на Boolean
, например, компилятор обнаруживает ошибку:
def save(customer: Customer): Future[Boolean] = ...
def foo: Future[Boolean] = save(customer1).map(_ => save(customer2)) // Compiler fails this
И для каждого другого типа не Unit
он не будет компилироваться (кроме Any
, потому что Future[Any]
является подтипом Any
по совпадению).
Итак, у компилятора есть специальные правила в этом случае? Или происходит более общий процесс?