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
).
Так есть ли у компилятора особые правила в этом случае? Или происходит более общий процесс?