Я предполагаю, что вы используете scala 2.13 с реализацией литеральных типов . Для этого объяснения вы можете рассматривать type
и class
как синонимы, но на самом деле это разные концепции.
Чтобы собрать все вместе, стоит рассматривать каждый примитивный тип как набор подтипов, каждый из которых представляет тип одного единственного буквального значения.
Итак, литерал 1
является значением и типом одновременно (экземпляр 1
типа 1
), и это подтип класса значений Int
.
Давайте докажем, что 1
является подтипом Int, используя 'неявно':
implicitly[1 <:< Int] // compiles
То же, но используя val
:
val one:1 = 1
implicitly[one.type <:< Int] // compiles
Итак one
- это вид экземпляр (объект) типа 1
(и одновременно экземпляр типа Int
, потому что Int
является супертипом 1
). Вы можете использовать это значение так же, как и любые другие объекты (передать его функции или присвоить другому vals
et c).
val one:1 = 1
val oneMore: 1 = one
val oneMoreGeneric: Int = one
val oneNew:1 = 1
Мы можем предположить, что все эти vals
содержат один и тот же экземпляр одного объекта, потому что с практической точки зрения не имеет значения, является это тем же объектом или нет.
Технически это вообще не объект, потому что примитивы возникли из мира java (JVM), где примитивы не являются объектами. Это разные сущности.
Scala язык пытается объединить эти две концепции в одну (все - классы), поэтому разработчикам не нужно слишком много думать о различиях.
Но вот некоторые различия за кулисами. Каждый класс значений является подтипом AnyVal
, но остальные классы являются подтипом AnyRef
(обычный класс).
implicitly[1 <:< AnyVal] //compiles
implicitly[Int <:< AnyVal] // compiles
trait AnyTraint
implicitly[AnyTraint <:< AnyVal] // fails to compail
implicitly[AnyTraint <:< AnyRef] // compiles
И, кроме того, из-за его неклассовой природы в JVM, вы не можете расширить value classes
как обычный класс или использовать new
для создания экземпляра (потому что scala компилятор эмулирует new
сам по себе). Вот почему с точки зрения расширения классов значений вы должны думать о них как о final и с точки зрения создания экземпляров вручную вы должны думать о них как о абстрактных . Но в большинстве других точек зрения он похож на любой другой обычный класс.
Итак, scala компилятор может своего рода расширять Int
на 1,2,3 ..
типы и создавать их экземпляры для vals
, но разработчики не могут сделать это вручную.