scalabook

Форк
0
/
types.md 
381 строка · 18.5 Кб

Типы данных

В этом разделе представлен обзор переменных и типов данных Scala.

Два вида переменных

Переменная может быть неизменяемой или изменяемой:

ТипОписание
val
Создает неизменяемую переменную, подобную final
в Java. Согласно стандартам Scala и функционального программирования желательно всегда создавать переменную с помощью val
.
var
Создает изменяемую переменную. Практически не используется в Scala, т.к. изменяемая переменная противоречит принципам функционального программирования.

В примере показано, как создавать val

и var
переменные:

val a = 0
// a: Int = 0
var b = 1
// b: Int = 1

Значение val

не может быть переназначено. Если попытаться переназначить, то будет получена ошибка компиляции:

val msg = "Hello, world"
msg = "Aloha"
// error:
// Reassignment to val msg
// msg = "Aloha"
// ^^^^^^^^^^^^^

И наоборот, var

может быть переназначен:

var msg = "Hello, world"
msg = "Aloha"
msg
// res2: String = "Aloha"

Объявление типов переменных

Когда создается переменная, можно явно объявить ее тип или позволить определить тип компилятору:

val x: Int = 1
val x = 1

Вторая форма известна как вывод типа (type inference), и это отличный способ помочь сохранить код кратким. Компилятор Scala обычно может определить тип данных, как показано в выходных данных этих примеров:

val x = 1
// x: Int = 1
val s = "a string"
// s: String = "a string"
val nums = List(1, 2, 3)
// nums: List[Int] = List(1, 2, 3)

Всегда можно явно объявить тип переменной, но в простых примерах, подобных этим, в этом нет необходимости:

val x: Int = 1
val s: String = "a string"
val p: Person = Person("Richard")

В Scala все значения имеют тип, включая числовые значения и функции.

Иерархия типов в Scala

Приведенная ниже диаграмма иллюстрирует подмножество иерархии типов.

иерархия типов

Any

- это супертип всех типов, также называемый the top type. Он определяет универсальные методы, такие как equals
, hashCode
и toString
.

У верхнего типа Any

есть подтип Matchable
, который используется для обозначения всех типов, для которых возможно выполнить pattern matching. Важно гарантировать вызов свойства "параметричность", что вкратце означает, что мы не можем сопоставлять шаблоны для значений типа Any
, а только для значений, которые являются подтипом Matchable
. Справочная документация содержит более подробную информацию о Matchable.

Matchable

имеет два важных подтипа: AnyVal
и AnyRef
.

AnyVal

представляет типы значений. Существует несколько предопределенных типов значений, и они non-nullable: Double
, Float
, Long
, Int
, Short
, Byte
, Char
, Unit
и Boolean
. Unit
- это тип значения, который не несет никакой значимой информации. Существует ровно один экземпляр Unit
- ()
.

AnyRef

представляет ссылочные типы. Все типы, не являющиеся значениями, определяются как ссылочные типы. Каждый пользовательский тип в Scala является подтипом AnyRef
. Если Scala используется в контексте среды выполнения Java, AnyRef
соответствует java.lang.Object
.

В языках, основанных на операторах, void

используется для методов, которые ничего не возвращают. В Scala для методов, которые не имеют возвращаемого значения, такие как следующий метод, для той же цели используется Unit
:

def printIt(a: Any): Unit = println(a)

Вот пример, демонстрирующий, что строки, целые числа, символы, логические значения и функции являются экземплярами Any

и могут обрабатываться так же, как и любой другой объект:

val list: List[Any] = List(
"a string",
732, // Число
'c', // Символ
'\'', // Экранированный символ
true, // булево значение
() => "анонимная функция, возвращающая строку"
)

Код определяет список значений типа List[Any]

. Список инициализируется элементами различных типов, но каждый из них является экземпляром scala.Any
, поэтому мы можем добавить их в список.

Типы значений в Scala

Как показано выше, числовые типы Scala расширяют AnyVal

, и все они являются полноценными объектами. В этих примерах показано, как объявлять переменные этих числовых типов:

val b: Byte = 1
val i: Int = 1
val l: Long = 1
val s: Short = 1
val d: Double = 2.0
val f: Float = 3.0

В первых четырех примерах, если явно не указать тип, то число 1

по умолчанию будет равно Int
, поэтому, если нужен один из других типов данных — Byte
, Long
или Short
— необходимо явно объявить эти типы. Числа с десятичной дробью (например, 2.0
) по умолчанию будут иметь значение Double
, поэтому, если необходим Float
, нужно объявить Float
явно, как показано в последнем примере.

Поскольку Int

и Double
являются числовыми типами по умолчанию, их можно создавать без явного объявления типа данных:

val i = 123
// i: Int = 123
val j = 1.0
// j: Double = 1.0

Также можно добавить символы L

, D
, and F
(или их эквивалент в нижнем регистре) для того, чтобы задать Long
, Double
, или Float
значения:

val x = 1_000L
// x: Long = 1000L
val y = 2.2D
// y: Double = 2.2
val z = -3.3F
// z: Float = -3.3F

Вы также можете использовать шестнадцатеричное представление для форматирования целых чисел (обычно это Int

, но также поддерживается суффикс L
для указания Long
):

val a = 0xACE // val a: Int = 2766
val b = 0xfd_3aL // val b: Long = 64826

Scala поддерживает множество различных способов форматирования одного и того же числа с плавающей запятой, например:

val q = .25 // val q: Double = 0.25
val r = 2.5e-1 // val r: Double = 0.25
val s = .0025e2F // val s: Float = 0.25

В Scala также есть типы String

(значение заключается в двойные кавычки или три двойных) и Char
(значение заключается в одинарные кавычки):

val name = "Bill"
// name: String = "Bill"
val c = 'a'
// c: Char = 'a'

BigInt и BigDecimal

Для действительно больших чисел можно использовать типы BigInt

и BigDecimal
:

var a = BigInt(1_234_567_890_987_654_321L)
// a: BigInt = 1234567890987654321
var b = BigDecimal(123_456.789)
// b: BigDecimal = 123456.789

Где Double

и Float
являются приблизительными десятичными числами, а BigDecimal
используется для точной арифметики, например, при работе с валютой.

BigInt

и BigDecimal
поддерживают все привычные числовые операторы:

val b = BigInt(1234567890)
// b: BigInt = 1234567890
val c = b + b
// c: BigInt = 2469135780
val d = b * b
// d: BigInt = 1524157875019052100

Строки

Строки Scala похожи на строки Java, но у них есть две замечательные дополнительные функции:

  • они поддерживают интерполяцию строк
  • создавать многострочные строки очень просто
String interpolation

Интерполяция строк обеспечивает очень удобный способ использования переменных внутри строк. Например, учитывая эти три переменные:

val firstName = "John"
val mi = 'C'
val lastName = "Doe"

их комбинацию можно получить так:

s"Name: $firstName $mi $lastName"
// res5: String = "Name: John C Doe"

Достаточно поставить перед строкой букву s

, а затем - символ $
перед именами переменных внутри строки.

Чтобы вставить произвольные выражения в строку, они заключаются в фигурные скобки:

s"2 + 2 = ${2 + 2}"
// res7: String = "2 + 2 = 4"
val x = -1
// x: Int = -1
s"x.abs = ${x.abs}"
// res8: String = "x.abs = 1"

Символ s

, помещенный перед строкой, является лишь одним из возможных интерполяторов. Если использовать f
вместо s
, можно использовать синтаксис форматирования в стиле printf
в строке. Кроме того, интерполятор строк - это всего лишь специальный метод, и его можно определить самостоятельно. Например, некоторые библиотеки баз данных определяют очень мощный интерполятор sql
.

Для экранирования символа "

в интерполяции используется символ $
:

val inventor = "Thomas Edison"
// inventor: String = "Thomas Edison"
val interpolation = s"as $inventor said: $"The three great essentials to achieve anything worth while are: Hard work, Stick-to-itiveness, and Common sense.$""
// interpolation: String = "as Thomas Edison said: \"The three great essentials to achieve anything worth while are: Hard work, Stick-to-itiveness, and Common sense.\""
println(interpolation)
// as Thomas Edison said: "The three great essentials to achieve anything worth while are: Hard work, Stick-to-itiveness, and Common sense."

Подробнее об интерполяции строк

Multiline strings

Многострочные строки создаются путем включения строки в три двойные кавычки:

println("""The essence of Scala:
Fusion of functional and object-oriented
programming in a typed setting.""")
// The essence of Scala:
// Fusion of functional and object-oriented
// programming in a typed setting.

Одним из недостатков базового подхода является то, что строки после первой имеют отступ.

Если важно исключить отступ, можно поставить символ |

перед всеми строками после первой и вызвать метод stripMargin
после строки:

println("""The essence of Scala:
|Fusion of functional and object-oriented
|programming in a typed setting.""".stripMargin)
// The essence of Scala:
// Fusion of functional and object-oriented
// programming in a typed setting.

Теперь все строки выравниваются по левому краю.

Здесь также можно использовать переменные внутри строки, добавив s

перед первыми """
.

Приведение типов

Типы значений могут быть приведены следующим образом:

приведение типов

Например:

val x: Int = 987654321
// x: Int = 987654321
val y: Long = x
// y: Long = 987654321L
val face: Char = '☺'
// face: Char = '☺'
val number: Int = face
// number: Int = 9786

Приведение типов однонаправленное, следующий код не будет компилиться:

val a: Long = 987654321
val b: Float = a
val c: Long = b
// val c: Long = b
// ^
// Found: (b : Float)
// Required: Long

Неявное приведение типов в некоторых случаях помечено как deprecated

и может быть запрещено в будущих версиях:

val x: Long = 987654321
val y: Float = x
// method long2float in object Long is deprecated since 2.13.1: Implicit conversion from Long to Float is dangerous because it loses precision. Write `.toFloat` instead.
// val y: Float = x
// ^

Nothing и null

Nothing

является подтипом всех типов, также называемым the bottom type. Нет значения, которое имело бы тип Nothing
. Он обычно сигнализирует о прекращении, таком как thrown exception
, выходе из программы или бесконечном цикле - т.е. это тип выражения, который не вычисляется до определенного значения, или метод, который нормально не возвращается.

Null

- это подтип всех ссылочных типов (т.е. любой подтип AnyRef
). Он имеет единственное значение, определяемое ключевым словом null
. В настоящее время применение null
считается плохой практикой. Его следует использовать в основном для взаимодействия с другими языками JVM. Опция opt-in compiler изменяет статус Null, делая все ссылочные типы non-nullable. Этот параметр может стать значением по умолчанию в будущей версии Scala.

В то же время null

почти никогда не следует использовать в коде Scala. Альтернативы null
обсуждаются в главе о функциональном программировании и в документации API.


Ссылки:

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.