Spec-Zone.ru › Kotlin 1.6

Основные типы

В Kotlin всё является объектом в том смысле, что мы можем вызывать методы и свойства для любой переменной. Некоторые типы могут иметь специальное внутреннее представление — например, числа, символы и булевы значения могут представляться примитивными значениями во время выполнения — но для пользователя они выглядят как обычные классы. В этом разделе мы описываем основные типы, используемые в Kotlin: числа, булевы значения, символы, строки и массивы.

Числа

Целочисленные типы

Kotlin предоставляет набор встроенных типов, представляющих числа.
Для целых чисел существуют четыре типа с различными размерами и, следовательно, диапазонами значений.

Тип

Размер (биты)

Минимальное значение

Максимальное значение

Byte

8

-128

127

Short

16

-32768

32767

Int

32

-2 147 483 648 (-231)

2 147 483 647 (231 - 1)

Long

64

-9 223 372 036 854 775 808 (-263)

9 223 372 036 854 775 807 (263 - 1)

Все переменные, инициализированные целыми значениями, не превышающими максимального значения Int, имеют тип Int. Если начальное значение превышает это значение, то тип является Long. Чтобы явно указать Long значение, добавьте суффикс L к значению.

val one = 1 // Int
val threeBillion = 3000000000 // Long
val oneLong = 1L // Long
val oneByte: Byte = 1

Типы с плавающей точкой

Для действительных чисел Kotlin предоставляет типы с плавающей точкой Float и Double. Согласно стандарту IEEE 754, типы с плавающей точкой различаются по своей точности, то есть по тому, сколько десятичных знаков они могут хранить. Float отражает одинарную точность IEEE 754, а Double обеспечивает двойную точность.

Тип

Размер (биты)

Значимые биты

Биты порядка

Десятичные знаки

Float

32

24

8

6-7

Double

64

53

11

15-16

Вы можете инициализировать переменные типа Double и Float числами с дробной частью. Она отделена от целой части точкой (.) Для переменных, инициализированных дробными числами, компилятор определяет тип Double.

val pi = 3.14 // Double
// val one: Double = 1 // Error: type mismatch
val oneDouble = 1.0 // Double

Чтобы явно указать тип Float для значения, добавьте суффикс f или F. Если такое значение содержит более 6-7 десятичных знаков, оно будет округлено.

val e = 2.7182818284 // Double
val eFloat = 2.7182818284f // Float, actual value is 2.7182817

Обратите внимание, что в отличие от некоторых других языков, в Kotlin нет неявных расширений типов для чисел. Например, функция с параметром Double может быть вызвана только со значениями Double, но не Float, Int или другими числовыми значениями.

fun main() {
    fun printDouble(d: Double) { print(d) }

    val i = 1    
    val d = 1.0
    val f = 1.0f 

    printDouble(d)
//    printDouble(i) // Error: Type mismatch
//    printDouble(f) // Error: Type mismatch
}

Для преобразования числовых значений в разные типы используйте явные преобразования.

Литеральные константы

Существуют следующие виды литеральных констант для целочисленных значений:

  • Десятичные: 123

    • Длинные числа отмечаются заглавной L: 123L

  • Шестнадцатеричные: 0x0F

  • Двоичные: 0b00001011

Восьмеричные литералы не поддерживаются.

Kotlin также поддерживает стандартную запись для чисел с плавающей точкой:

  • Вещественные числа по умолчанию: 123.5, 123.5e10

  • Числа с плавающей точкой типа float отмечаются f или F: 123.5f

Можно использовать нижние подчеркивания для повышения читаемости числовых констант:

val oneMillion = 1_000_000
val creditCardNumber = 1234_5678_9012_3456L
val socialSecurityNumber = 999_99_9999L
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010

Представление чисел на JVM

На платформе JVM числа хранятся как примитивные типы: int, double, и так далее. Исключением являются случаи, когда вы создаете ссылку на число со значением null, например Int?, или используете обобщения. В этих случаях числа упаковываются в классы Java Integer, Double, и так далее.

Обратите внимание, что ссылки на одно и то же число со значением null могут быть разными объектами:

fun main() {
//sampleStart
    val a: Int = 100
    val boxedA: Int? = a
    val anotherBoxedA: Int? = a
    
    val b: Int = 10000
    val boxedB: Int? = b
    val anotherBoxedB: Int? = b
    
    println(boxedA === anotherBoxedA) // true
    println(boxedB === anotherBoxedB) // false
//sampleEnd
}

Все ссылки на null-значения a на самом деле являются одним и тем же объектом из-за оптимизации памяти, применяемой JVM для Integer между -128 и 127. Она не применяется к ссылкам b, поэтому они являются разными объектами.

С другой стороны, они всё ещё равны:

fun main() {
//sampleStart
    val b: Int = 10000
    println(b == b) // Prints 'true'
    val boxedB: Int? = b
    val anotherBoxedB: Int? = b
    println(boxedB == anotherBoxedB) // Prints 'true'
//sampleEnd
}

Явные преобразования

Из-за разных представлений меньшие типы не являются подтипами больших. Если бы они были, у нас возникли бы проблемы следующего типа:

// Hypothetical code, does not actually compile:
val a: Int? = 1 // A boxed Int (java.lang.Integer)
val b: Long? = a // implicit conversion yields a boxed Long (java.lang.Long)
print(b == a) // Surprise! This prints "false" as Long's equals() checks whether the other is Long as well

Таким образом, равенство было бы потеряно незаметно, не говоря уже о тождественности.

Следовательно, меньшие типы не преобразуются неявно в большие. Это означает, что присвоение значения типа Byte переменной типа Int требует явного преобразования.

fun main() {
//sampleStart
    val b: Byte = 1 // OK, literals are checked statically
    // val i: Int = b // ERROR
    val i1: Int = b.toInt()
//sampleEnd
}

Все числовые типы поддерживают преобразования в другие типы:

  • toByte(): Byte

  • toShort(): Short

  • toInt(): Int

  • toLong(): Long

  • toFloat(): Float

  • toDouble(): Double

  • toChar(): Char

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

val l = 1L + 3 // Long + Int => Long

Операции

Kotlin поддерживает стандартный набор арифметических операций над числами: +, -, *, /, %. Они объявлены как члены соответствующих классов.

fun main() {
//sampleStart
    println(1 + 2)
    println(2_500_000_000L - 1L)
    println(3.14 * 2.71)
    println(10.0 / 3)
//sampleEnd
}

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

Деление целых чисел

Деление целых чисел всегда возвращает целое число. Любая дробная часть отбрасывается.

fun main() {
//sampleStart
    val x = 5 / 2
    //println(x == 2.5) // ERROR: Operator '==' cannot be applied to 'Int' and 'Double'
    println(x == 2)
//sampleEnd
}

Это верно для деления между любыми двумя целочисленными типами.

fun main() {
//sampleStart
    val x = 5L / 2
    println(x == 2L)
//sampleEnd
}

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

fun main() {
//sampleStart
    val x = 5 / 2.toDouble()
    println(x == 2.5)
//sampleEnd
}

Битовые операции

Kotlin предоставляет набор битовых операций над целочисленными числами. Они работают на битовом уровне напрямую с битами представления чисел. Битовые операции представлены функциями, которые могут вызываться в инфиксной форме. Они могут быть применены только к Int и Long.

val x = (1 shl 2) and 0x000FF000

Вот полный список битовых операций:

  • shl(bits) — сдвиг влево со знаком

  • shr(bits) — сдвиг вправо со знаком

  • ushr(bits) — сдвиг вправо без знака

  • and(bits) — побитовое и

  • or(bits) — побитовое или

  • xor(bits) — побитовое исключающее или

  • inv() — инверсия битов

Сравнение чисел с плавающей точкой

Операции с числами с плавающей точкой, обсуждаемые в этом разделе:

  • Проверки на равенство: a == b и a != b

  • Операторы сравнения: a < b, a > b, a <= b, a >= b

  • Инициализация и проверки диапазонов: a..b, x in a..b, x !in a..b

Когда операнды a и b статически известны как Float или Double или их nullable аналоги (тип объявлен, выведен или является результатом умного приведения типов), операции над числами и формируемый ими диапазон следуют стандарту IEEE 754 для арифметики чисел с плавающей точкой.

Однако, для поддержки общих случаев использования и обеспечения полного упорядочения, когда операнды не являются статически типизированными как числа с плавающей точкой (например, Any, Comparable<...>, параметр типа), операции используют реализации equals и compareTo для Float и Double, которые отличаются от стандарта, так что:

  • NaN считается равным самому себе

  • NaN считается больше любого другого элемента, включая POSITIVE_INFINITY

  • -0.0 считается меньше 0.0

Целые без знака

В дополнение к целым типам, Kotlin предоставляет следующие типы для целых чисел без знака:

  • UByte: целое без знака 8 бит, диапазон от 0 до 255

  • UShort: целое без знака 16 бит, диапазон от 0 до 65535

  • UInt: целое без знака 32 бит, диапазон от 0 до 2^32 - 1

  • ULong: целое без знака 64 бит, диапазон от 0 до 2^64 - 1

Типы без знака поддерживают большинство операций своих знаковых аналогов.

Изменение типа с беззнакового на знаковый (и наоборот) — это бинарно несовместимое изменение.

Массивы и диапазоны без знака

Массивы без знака и операции над ними находятся в бета-версии. Они могут быть изменены несовместимо в любое время. Требуется опциональное включение (см. подробности ниже).

Как и с примитивами, каждый тип без знака имеет соответствующий тип, представляющий массивы этого типа:

  • UByteArray: массив байтов без знака

  • UShortArray: массив коротких целых без знака

  • UIntArray: массив целых без знака

  • ULongArray: массив длинных целых без знака

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

При использовании массивов без знака вы получите предупреждение, указывающее, что эта функция еще не является стабильной. Чтобы удалить предупреждение, включите ее с помощью аннотации @ExperimentalUnsignedTypes. Решение о том, должны ли ваши клиенты явно включить использование вашего API, зависит от вас, но помните, что массивы без знака не являются стабильной функцией, поэтому API, использующий их, может быть сломан изменениями в языке. Узнайте больше о требованиях к включению.

Диапазоны и прогрессии поддерживаются для UInt и ULong классами UIntRange, UIntProgression, ULongRange, и ULongProgression. Вместе с типами целых чисел без знака эти классы являются стабильными.

Литералы

Для упрощения работы с целыми числами без знака Kotlin предоставляет возможность маркировать целочисленный литерал суффиксом, указывающим конкретный тип без знака (аналогично Float или Long)

  • u и U отмечают беззнаковые литералы. Точный тип определяется на основе ожидаемого типа. Если ожидаемый тип не указан, компилятор использует UInt или ULong в зависимости от размера литерала.

val b: UByte = 1u  // UByte, expected type provided
val s: UShort = 1u // UShort, expected type provided
val l: ULong = 1u  // ULong, expected type provided

val a1 = 42u // UInt: no expected type provided, constant fits in UInt
val a2 = 0xFFFF_FFFF_FFFFu // ULong: no expected type provided, constant doesn't fit in UInt
  • uL и UL явно отмечают литерал как беззнаковый long.

val a = 1UL // ULong, even though no expected type provided and constant fits into UInt

Дополнительное обсуждение

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

Булевы

Тип Boolean представляет булевы объекты, которые могут иметь два значения: true и false.

Boolean имеет nullable аналог Boolean?, который также имеет значение null

Встроенные операции с булевыми значениями включают:

  • || — дизъюнкция (логическое ИЛИ)

  • && — конъюнкция (логическое И)

  • ! — отрицание (логическое НЕ)

|| и && работают лениво.

fun main() {
//sampleStart
    val myTrue: Boolean = true
    val myFalse: Boolean = false
    val boolNull: Boolean? = null
    
    println(myTrue || myFalse)
    println(myTrue && myFalse)
    println(!myTrue)
//sampleEnd
}

На JVM: nullable ссылки на булевы объекты упаковываются аналогично числам.

Символы

Символы представлены типом Char. Литералы символов заключаются в одинарные кавычки: '1'

Специальные символы начинаются с обратной косой черты \. Поддерживаются следующие последовательности экранирования: \t, \b, \n, \r, \', \", \\ и \$.

Для кодирования любого другого символа используйте синтаксис Unicode-экранирования: '\uFF00'.

fun main() {
//sampleStart
    val aChar: Char = 'a'
 
    println(aChar)
    println('\n') //prints an extra newline character
    println('\uFF00')
//sampleEnd
}

Если значение символьной переменной — это цифра, вы можете явно преобразовать ее в число Int с помощью функции digitToInt().

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

Строки

Строки в Kotlin представлены типом String. В общем случае, строковое значение представляет собой последовательность символов в двойных кавычках (").

val str = "abcd 123"

Элементы строки — это символы, к которым можно получить доступ с помощью операции индексирования: s[i]. Вы можете перебирать эти символы с помощью цикла for:

fun main() {
val str = "abcd"
//sampleStart
for (c in str) {
    println(c)
}
//sampleEnd
}

Строки неизменяемы. После инициализации строки вы не можете изменить её значение или присвоить новое значение. Все операции, которые преобразуют строки, возвращают результаты в новом String объекте, оставляя исходную строку неизменной.

fun main() {
//sampleStart
    val str = "abcd"
    println(str.uppercase()) // Create and print a new String object
    println(str) // the original string remains the same
//sampleEnd
}

Для конкатенации строк используйте оператор +. Это также работает для конкатенации строк со значениями других типов, если первый элемент в выражении — строка:

fun main() {
//sampleStart
val s = "abc" + 1
println(s + "def")
//sampleEnd
}

Обратите внимание, что в большинстве случаев предпочтительнее использовать шаблоны строк или сырые строки, чем конкатенацию строк.

Литералы строк

Kotlin имеет два типа литералов строк:

  • Экранированные строки, которые могут содержать экранированные символы

  • Сырые строки, которые могут содержать символы новой строки и произвольный текст

Вот пример экранированной строки:

val s = "Hello, world!\n"

Экранирование выполняется стандартным способом, с помощью обратной косой черты (\). Список поддерживаемых последовательностей escape-символов см. выше в разделе Символы.

Сырая строка ограничена тройной кавычкой ("""). Она не поддерживает экранирование и может содержать символы новой строки и любые другие символы:

val text = """
    for (c in "foo")
        print(c)
"""

Чтобы удалить начальные пробелы из сырых строк, используйте функцию trimMargin():

val text = """
    |Tell me and I forget.
    |Teach me and I remember.
    |Involve me and I learn.
    |(Benjamin Franklin)
    """.trimMargin()

По умолчанию, | используется в качестве префикса отступа, но вы можете выбрать другой символ и передать его в качестве параметра, например trimMargin(">").

Шаблоны строк

Литералы строк могут содержать шаблонные выражения — части кода, которые оцениваются, и результаты которых конкатенируются в строку. Шаблонное выражение начинается с знака доллара ($) и состоит либо из имени:

fun main() {
//sampleStart
    val i = 10
    println("i = $i") // prints "i = 10"
//sampleEnd
}

либо из выражения в фигурных скобках:

fun main() {
//sampleStart
    val s = "abc"
    println("$s.length is ${s.length}") // prints "abc.length is 3"
//sampleEnd
}

Вы можете использовать шаблоны как в сырых, так и в экранированных строках. Чтобы вставить символ $ в сырую строку (которая не поддерживает экранирование обратной косой чертой) перед любым символом, который разрешен в качестве начала идентификатора, используйте следующий синтаксис:

val price = """
${'$'}_9.99
"""

Массивы

Массивы в Kotlin представлены классом Array. Он имеет функции get и set, которые преобразуются в [] по соглашениям перегрузки операторов, а также свойство size, наряду с другими полезными методами:

class Array<T> private constructor() {
    val size: Int
    operator fun get(index: Int): T
    operator fun set(index: Int, value: T): Unit

    operator fun iterator(): Iterator<T>
    // ...
}

Для создания массива используйте функцию arrayOf() и передайте в неё значения элементов, так что arrayOf(1, 2, 3) создаёт массив [1, 2, 3]. В качестве альтернативы, функция arrayOfNulls() может быть использована для создания массива заданного размера, заполненного null элементами.

Другой вариант — использовать конструктор Array, который принимает размер массива и функцию, возвращающую значения элементов массива, учитывая его индекс:

fun main() {
//sampleStart
    // Creates an Array<String> with values ["0", "1", "4", "9", "16"]
    val asc = Array(5) { i -> (i * i).toString() }
    asc.forEach { println(it) }
//sampleEnd
}

Как мы говорили выше, операция [] означает вызовы методов get() и set().

Массивы в Kotlin — неизменяемые. Это означает, что Kotlin не позволяет нам присваивать массив Array<String> массиву Array<Any>, что предотвращает возможные ошибки во время выполнения (но вы можете использовать Array<out Any>, см. Проекции типов).

Массивы примитивных типов

Kotlin также имеет классы, которые представляют массивы примитивных типов без накладных расходов на бокс: ByteArray, ShortArray, IntArray, и так далее. Эти классы не имеют отношения к наследованию к классу Array, но имеют тот же набор методов и свойств. Каждый из них также имеет соответствующую фабричную функцию:

val x: IntArray = intArrayOf(1, 2, 3)
x[0] = x[1] + x[2]
// Array of int of size 5 with values [0, 0, 0, 0, 0]
val arr = IntArray(5)

// e.g. initialise the values in the array with a constant
// Array of int of size 5 with values [42, 42, 42, 42, 42]
val arr = IntArray(5) { 42 }

// e.g. initialise the values in the array using a lambda
// Array of int of size 5 with values [0, 1, 2, 3, 4] (values initialised to their index value)
var arr = IntArray(5) { it * 1 } 
Последнее изменение: 07 апреля 2022 г.
Правила кодирования Проверка типов и преобразования типов

© 2010–2022 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/basic-types.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API