Объясни разницу между Any, AnyRef, AnyVal, Unit, Nothing, Null

В Scala система типов устроена иерархически. Все типы наследуются от универсального супертипа Any. Он разделяется на два основных подтипа: AnyVal (все значения-примитивы) и AnyRef (все ссылочные объекты, как в Java). В дополнение к ним существуют особые типы: Unit, Null и Nothing, каждый из которых имеет особое предназначение. Ниже разберём каждый из них максимально подробно:

Any

Any — это самый общий супертип всех типов в Scala. Он включает как значимые типы (примитивы, такие как Int, Double, Boolean), так и ссылочные типы (все классы и объекты). Все типы в Scala неявно наследуются от Any.

Методы, определённые в Any:

  • \==, != — сравнение значений

  • equals, hashCode — эквиваленты Java

  • toString — преобразование в строку

Пример:

val x: Any = 42
val y: Any = "Scala"
val z: Any = true

AnyVal

AnyVal — подтип Any, представляющий все примитивные типы, хранящиеся в стеке, а не в куче (как в Java). Это позволяет избежать накладных расходов на объекты. К AnyVal относятся:

  • Int

  • Double

  • Float

  • Long

  • Short

  • Byte

  • Boolean

  • Char

  • Unit

Это неизменяемые, производительные типы. Scala допускает создание собственных типов-значений, наследующих AnyVal, через механизм value classes.

Пример:

val a: AnyVal = 10

AnyRef

AnyRef — подтип Any, который соответствует всем ссылочным типам. Это обёртка над java.lang.Object, то есть все классы, объекты и ссылки на них в Scala (в том числе те, что импортированы из Java) являются подтипами AnyRef.

Все пользовательские классы в Scala по умолчанию являются подтипами AnyRef.

Пример:

class User(val name: String)
val u: AnyRef = new User("Alice")

Scala на JVM: AnyRef = java.lang.Object.

Unit

Unit — аналог void в Java. Он означает: "значимого значения нет", но возвращается одно-единственное значение () (пустой кортеж). Используется, когда функция ничего не возвращает, но всё же должна вернуть какой-то тип (например, в функциональной композиции).

Пример:

def log(msg: String): Unit = println(msg)
val result: Unit = log("Hello") // result = ()

Unit — подтип AnyVal, и это означает, что он не объект, а значение, аналогичное примитивам.

Nothing

Nothing — самый нижний тип в иерархии. Он является подтипом любого другого типа. Это означает, что Nothing может быть подставлен в любое место, где ожидается значение любого типа.

Однако нет ни одного значения типа Nothing, и он используется только в особых ситуациях:

  • выражения, которые никогда не завершаются, например, throw, sys.error, бесконечная рекурсия;

  • как сигнал об ошибке в функциях (особенно в Option, Either, Try);

  • в параметрических типах, когда надо показать отсутствие значения.

Примеры:

def fail(msg: String): Nothing = throw new RuntimeException(msg)
val x: String = fail("oops") // тип x  String, но значение не будет получено
val emptyList: List\[Nothing\] = List()

List[Nothing] может использоваться как универсальный пустой список: List[String], List[Int], и т. д.

Null

Null — это тип, который имеет единственное значение null. Является подтипом всех ссылочных типов (AnyRef), но не подтипом значимых типов (AnyVal).

Используется только для обеспечения совместимости с Java. В чисто функциональном стиле его следует избегать — предпочтение отдаётся Option.

Пример:

val x: String = null // OK
val y: Int = null // Ошибка: Null не подтип Int

Можно использовать Null в аннотациях типов, например:

val str: String | Null = "Scala"

Scala 3 (Dotty) добавляет опциональную нулевую безопасность, при которой null допустим только явно.

Сводная таблица

Тип Родитель Пример значения Назначение
Any Суперкласс всех типов
--- --- --- ---
AnyVal Any 42, true Все примитивные типы
--- --- --- ---
AnyRef Any "abc", List() Все ссылочные типы
--- --- --- ---
Unit AnyVal () Отсутствие значимого значения
--- --- --- ---
Nothing подтип всех Тип, не содержащий ни одного значения
--- --- --- ---
Null подтип AnyRef null Представляет ссылку на ничто
--- --- --- ---

Таким образом, типы Any, AnyRef, AnyVal, Unit, Nothing и Null составляют фундамент Scala-типизации и дают гибкие, но безопасные возможности для обобщения, исключений, совместимости с Java и функционального программирования.