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