En Kotlin todo es un objeto en el sentido de que podemos llamar a las funciones o propiedades de cualquier variable. Algunos tipos pueden tener una representación interna especial – por ejemplo, los números, letras y booleanos pueden ser representadas como valores primitivos en tiempo de ejecución – sin embargo para el usuario se ven como clases ordinarias. En esta sección vamos a ver los tipos básicos usados en Kotlin: números (numbers), letras (chars), booleanos (booleans), matrices (arrays) y cadenas de texto (strings).
Kotlin manipula los números de una forma similar a la de Java, pero no es exactamente la misma. Por ejemplo, no hay conversiones extendidas implícitas para los números, y los literales son ligeramente diferentes en algunos casos.
Kotlin proporciona los siguientes tipos de números:
Type | Bit width |
---|---|
Double | 64 |
Float | 32 |
Long | 64 |
Int | 32 |
Short | 16 |
Byte | 8 |
Las letras no son números en Kotlin.
Existen los siguientes tipos de constantes literales:
123
Long
añadiríamos la letra L
en mayúscula: 123L
0x0F
0b00001011
Kotlin también tiene soporte para números de coma flotante:
123.5
or 123.5e10
f
o F
: 123.5f
Puedes utilizar guiones bajos para facilitar la lectura de un número en Kotlin, como si estuvieses simulando puntos (o comas, en función de la región):
val millon = 1_000_000 val tarjetaCredito = 1234_5678_9012_3456L val seguridadSocial = 999_99_9999L val hexBytes = 0xFF_EC_DE_5E val bytes = 0b11010010_01101001_10010100_10010010
En la plataforma Java, los números se almacenan como tipos primitivos JVM a menos que necesitemos una referencia numérica nula (por ejemplo Int?
) o sean genéricos. En estos casos los números están encajonados.
Ten en cuenta que encajonar los números no preserva la identidad necesariamente:
fun main() { //sampleStart val numero: Int = 10000 println(numero === numero) // "true" val numeroEncajonado: Int? = numero val otroNumeroEncajonado: Int? = numero println(numeroEncajonado === otroNumeroEncajonado) // "false" //sampleEnd }
Por otro lado, preserva la igualdad:
fun main() { //sampleStart val numero: Int = 10000 println(numero == numero) // "true" val numeroEncajonado: Int? = numero val otroNumeroEncajonado: Int? = numero println(numeroEncajonado == otroNumeroEncajonado) // "true" //sampleEnd }
Debido a las diferentes representaciones, los tipos más pequeños no son subtipos de los más grandes. Si lo fueran, habría problemas como los siguientes:
fun main() { //sampleStart // Este código no compilaría val numeroInt: Int? = 1 // Un Int encajonado val numeroLong: Long? = numeroInt // Conversión implícita print(numeroLong == numeroInt) // "false" //sampleEnd }
La igualdad se habría perdido por completo, sin mencionar la identidad.
Como consecuencia, los tipos más pequeños no son convertidos implicitamente a tipos más grandes. Lo cual significa que no podemos asignar un valor de tipo Byte
a una variable Int
sin una conversión explícita.
fun main() { //sampleStart val numero1: Byte = 1 // Funcionaría ya que los literales se comprueban estáticamente val numero2: Int = numero1 // Error //sampleEnd }
Podemos utilizar conversiones explicitas para extender los números.
fun main() { //sampleStart val numero1: Byte = 1 val numero2: Int = numero1.toInt() // No daría error print(numero2) // "1" //sampleEnd }
Cada tipo de número soporta las siguientes convesiones:
toByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
ToFloat(): Float
toDouble(): Double
toChar(): Char
La ausencia de conversiones implícitas raramente se nota ya que el tipo se deduce del contexto y las operaciones aritméticas están sobrecargadas para realizar las conversiones apropiadas, por ejemplo:
val numero = 1L + 3 // Long + Int => Long
Kotlin soporta un conjunto estándar de operaciones aritméticas sobre números, declaradas como miembros de las clases apropiadas (el compilador optimiza las llamadas a las instrucciones correspondientes).
Como operaciones bit a bit (bitwise), no hay caracteres especiales para ellas, solo fuciones con nombre que se pueden llamar de forma infija (infix), por ejemplo:
val x = (1 shl 2) and 0x000FF000
Lista completa de operaciones bit a bit (disponible solo para Int
y Long
:
shl(bits)
– signed shift left (Java’s <<
)shr(bits)
– signed shift right (Java’s >>
)ushr(bits)
– unsigned shift right (Java’s >>>
)and(bits)
– bitwise andor(bits)
– bitwise orxor(bits)
– bitwise xorinv()
– bitwise inversionLas operaciones sobre números de coma flotante son:
a == b
y a != b
a < b
, a > b
, a <= b
, a >= b
a..b
, x in a..b
, x !in a..b
Cuando los operandos a
y b
son estáticamente conocidos como Float
o Double
o sus equivalentes aceptan valores nulos (el tipo es declarado o deducido es resultado de una conversión inteligente (smart cast)), las operaciones sobre los números y el rango que forman sigue el estándar IEEE 754 de la aritmética de coma flotante.
Sin embargo, para admitir casos de usos genéricos y proporcionar ordenamiento total, cuando los operandos no se escriben de forma estática como números de coma flotante (por ejemplo Any
, Comparable<...>
, un parámetro detipo), las operaciones usan las implementaciones equals
y compareTo
para Float
y Double
, las cuales discrepan con el estándar, por lo que:
NaN
se considera igual a si mismoNaN
se considera más grande que cualquier otro elemento, incluyendo POSITIVE_INFINITY
-0.0
se considera menor que 0.0
Los caracteres se representan mediante el tipo Char
. No pueden ser tratados directamente como números.
//sampleStart fun comprobar(caracter: Char) { if (caracter == 1) { // ERROR: Tipos incompatibles // ... } } //sampleEnd fun main() { comprobar('A') }
Los literales de tipo carácter se escriben con comillas simples: '1'
. Los caracteres especiales se pueden escribir mediante la barra invertida. Se soportan las siguientes secuencias: \t
, \b
, \n
, \r
, \'
, \"
, \\
y \$
. Para codificar cualquier otro carácter, utiliza la sintaxis Unicode: '\uFF00
‘.
Podemos convertir explicitamente un Char
a un número Int
:
//sampleStart fun valorDigitoDecimal(caracter: Char): Int { if (caracter !in '0'..'9') throw IllegalArgumentException("Fuera de rango") return caracter.toInt() - '0'.toInt() // Conversiones explicitas a números } //sampleEnd fun main() { println(valorDigitoDecimal('1')) }
Como los números, los caracteres están encajonados (boxed) cuando se necesita una referencia que puede ser nula. La identidad no se conserva por la operación de encajonado.
El tipo Boolean
representa a los booleanos y tiene dos valores: true
y false
.
Los boleanos están encajonados si se necesita una referencia que puede ser nula.
Las operaciones incorporadas en los booleanos incluyen:
||
– disyunción perezosa (lazy disjunction)&&
– conjunción perezosa (lazy conjunction)!
– negaciónLos arrays en Kotlin se representan mediante la clase Array
, la cual tiene las funciones get
y set
(que se convierten en [] por las convenciones de sobrecarga del operador), y propiedad de tamaño, junto con algunas otras funciones miembros útiles:
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> // ... }
Para crear un array, podemos usar la función de la biblioteca arrayOf()
y pasarle los valores, es decir, arrayOf(1, 2, 3)
crea un array [1, 2, 3]
. Alternativamente, la función arrayOfNulls()
puede ser utilizada para crear un array de un número dado lleno de elementos nulos.
Otra opción es utilizar el constructor Array
que toma el tamaño del array y la función que devuelve el valor inicial de cada elemento del array dado su índice:
fun main() { //sampleStart // Crear un Array<String>con los valores ["0", "1", "4", "9", "16"] val arrayAscendente = Array(5, { i -> (i * i).toString() }) arrayAscendente.forEach { println(it) } //sampleEnd }
Como hemos dicho antes, la operación []
representa las llamadas a las funciones get()
y set()
.
Nota: A diferencia de Java, los arrays en Kotlin son invariantes. Esto significa que kotlin no nos permite asignar un Array<String>
a un Array<Any>
, lo cual previene un posible fallo en tiempo de ejecución (pero puedes utilizar Array<out Any>
.
Kotlin también tiene clases especializadas en representar arrays de tipos primitivos: ByteArray
, ShortArray
, IntArray
, etc. Estas clases no tienen relación de herencia con la clase Array
, pero tienen el mismo conjunto de métodos y propiedades. Cada una de ellas también tiene la correspondiente función de creación:
val arrayDeEnteros: IntArray = intArrayOf(1, 2, 3) arrayDeEnteros[0] = arrayDeEnteros[1] + arrayDeEnteros[2]
Kotlin ha introducido los siguientes tipos de enteros sin firmar:
kotlin.UByte
: un entero de 8 bit sin firmar, rango de 0 a 255kotlin.UShort
: un entero de 16 bit sin firmar, rango de 0 a 65535kotlin.UInt
: un entero de 8 bit sin firmar, rango de 0 a 2^32-1kotlin.ULong
: un entero de 8 bit sin firmar, rango de 0 a 2^64-1Los tipos sin firmar soportan la mayoría de las operaciones de sus análogos firmados.
Los tipos sin firmar implementan otra característica, las clases en línea (inline classes).
Igual que con los primitivos, cada tipo sin firmar tiene un tipo correspondiente que representa un array:
kotlin.UByteArray
kotlin.UShoprtArray
kotlin.UIntArray
kotlin.ULongArray
También tienen una API similar a la clase Array
sin sobrecarga.
Además, las progresiones y los rangos son soportados para UInt
y ULong
por las clases:
kotlin.ranges.UIntRange
kotlin.ranges.UIntProgression
kotlin.ranges.UIntProgression
kotlin.ranges.UIntProgression
Para hacer los enteros sin firmar más fáciles de utilizar, Kotlin proporciona un método para etiquetar un literal entero con un sufijo indicando el tipo sin firmar específico (similar a Float/Long):
u
y U
etiquetan al literal como sin firmar. El tipo exacto será determinado basado en el tipo esperado. Si no se proporciona tipo UInt
o ULong
serán elegidos en base del tamaño del literal.uL
y UL
etiqueta de forma explicita el literal como un long sin firmar.Los strings se representan por el tipo String
. Los strings son inmutables. Los elementos de un string son caracteres a los cuales se puede acceder mediante la operación de indexación: myString[i]
. Un string puede ser iterado con un bucle for:
fun main() { //sampleStart val myString = "hola" for (character in myString) { print(character) } // "hola" //sampleEnd }
Puedes concatenar strings con el operador +
. También funciona para concatenar strings con valores de otro tipo, mientras el valor del primer elemento en la expresión sea una string:
fun main() { //sampleStart val myString = "El número " + 1 println(myString + " es el primer número natural") // "El número 1 es el primer número natural" //sampleEnd }
Recuerda que también puedes utilizar las Plantillas de Strings, algo recomendado en vez de utilizar la concatenación. Estas plantillas las verás un poco más abajo en esta página.
Kotlin tiene dos tipos de literales string:
val myString = "Hello, world!\n"
De esa forma podemos introducir un salto de línea. El “scaping” se consigue con una barra invertida.
Un string en bruto se delimita por una triple comilla """
, la cual no contiene ningún carácter de escape y puede contener nuevas líneas y otros caracteres:
val text = """ for (character in "Hola") print(c) """
Puedes eliminar el espacio en blanco con la función trimMargin()
.
val text = """ |Hola. |¿Cómo estás?. """.trimMargin()
Por defecto |
se utiliza como prefijo margen, pero puedes utilizar otro caracter y pasarle el parámetro, por ejemplo trimMargin(">")
.
Para evitar la concatenación, puedes crear una plantilla de texto con el símbolo del dolar $myString
. Y si quieres usar sus métodos utilizarías ${myString.método}
.
fun main() { //sampleStart val myString = "hola" println("La longitud de $myString es ${myString.length}") // "La longitud de hola es 4" //sampleEnd }
Las plantillas se pueden utilizar tanto en string en bruto como en strings de escape:
fun main() { //sampleStart val precio = """${'$'}9.99""" println(precio) //sampleEnd }
En esta lección aprenderás a declarar variables y los tipos básicos.
La mejor forma de aprender algo en esta vida es a base de practicar. Espero…
La principal forma de iniciar una corrutina en Kotlin es con el coroutine builder launch…
Las coroutines en Kotlin vienen a tratar de solucionar todos los problemas y dificultades que…
Con este esquema te puedes guiar a la hora de elegir el modismo o función…
El último que queda por ver es with qué en inglés significa "con". Por lo…