3.1. Types
As we have already discussed, Kotlin performs type checking at compile time. However, this does not imply that the types must always be declared explicitly: Kotlin can automatically infer types at compile time by analyzing how variables are initialized. When it is not possible to deduce the type of a variable, the compiler will emit an error message requesting an explicit type declaration. The following subsections present several examples.
3.1.1. Basic types
Kotlin can manipulate different data types. The simplest are integers (Byte, Short, Int, Long), floating point numbers (Float, Double), Booleans (Boolean with true
or false
values), and characters (Char).
Each numeric type uses a different number of bits for storing data. That is, each one can encode a different range of values and, in the case of floating-point numbers, achieves a different level of precision:
Integer numbers
Type | Symbol | Size (bits) | Minimum | Maximum |
---|---|---|---|---|
Byte | 8 | -2^7=128 | 2^7-1 = 127 | |
Short | 16 | -2^8= -32768 | 2^15-1=32767 | |
Int (default type) | 32 | -2^31 = -2*10^9 | 2^31-1 = 2*10^9 | |
Long | L | 64 | -2^63 = -9*10^18 | 2^63-1 = 9*10^18 |
Floating-point numbers
Type | Symbol | Size (bits) | Precision |
---|---|---|---|
Float | f / F | 32 | Single precision |
Double (default type) | 64 | Double precision |
Keyword val
in front of a variable declaration indicates that its value cannot be modified. That is, we are indicating that the symbol acts as a constant. On the other hand, keyword var
indicates that its value can be modified. We can explicitly declare the type of a variable:
var x: Int = 1
On the other hand, we can also let the Kotlin compiler infer the type automatically from the initialization. To infer the type, Kotlin chooses the smallest type (starting from Int
) that can contain the value of the variable.
var y = 1 // Int
val z = 3000000000 // Long
In this case, variable y
is of type Int
. Meanwhile, constant z
is of type Long
because the value is too large to fit in an Int
variable.
If we want a variable to have a specific type, it is best to explicitly declare that type.
val n: Byte = 15
For numeric variables, the Long
type can be forced by using the L
suffix after the constant value.
val j = 1L // Long
Symbol ?
in a variable declaration indicates that it can take the null value (it is nullable).
var tmp: String? = null
The following code produces an error because Kotlin cannot deduce the type within the variable declaration.
var tmp
tmp = "thing"
In Kotlin all basic types are also considered classes (in an object-oriented programming way). At runtime, these types will become simpler structures to improve performance.
3.1.2. Special types
Type Any
represents all types in Kotlin. Variables declared as nullable cannot be assigned to variables of type Any
.
var tmp1: String? = null
var tmp2: String = “help”
var v: Any
For example, the following assignment fails because tmp1
was declared to be nullable.
v = tmp1
However, the second assignment is correct.
v = tmp2
3.1.3. The String data type
A string stores a sequence of characters. We can define string literals in two different ways:
- Single-line string literals with escape sequences for special characters for line breaks, tabs, etc.
val s = "My, cat \n"
- Multiline string literals without escape sequences.
var text = """
You have two children
Tell me about them
""".trimMargin()
The blank spaces in front of You
and Tell
are included as part of the string. If we don’t want this to happen, we need to remove them explicitly.
var text = """
|You have two children
|Tell me about them
""".trimMargin()
The +
operator can be used to concatenate strings.
val text = "hello " + "friend"
3.1.3.1. String templates
Kotlin can insert the value of a variable within a string.
var mail_count = 3
var message = “You have $mail_count mails”
Moreover, Kotlin can also insert the result of evaluating an expression within a string:
var points = 3
var message = "You have two times points ${2*points}"
To use the dollar ($
) symbol within a string, we must use the escape sequence \$
.
var msg = "\$t"
For more complex formatting, we can use the format
method from class String.
var msg2 = String.format("%.2f",10.708)
Floating point numbers are rounded by default to the nearest value. In this example, the format
method would return the string “10.71”
.
3.1.4. Type casts
Kotlin does not perform automatic type casts. For example, if a method expects to receive a value of type Long
as a parameter, we cannot call the method with a value of type Int
. The same is true for floating point numbers: there is no implicit cast from Float
to Double
.
Nevertheless, Kotlin offers methods to convert a value of one basic type to a value of another basic type. For example:
val str:String = 4.toString()
// Correct: str <- "4"
Like toString()
, methods such as toLong()
or toDouble()
convert numbers between different numeric types.
On the other hand, if we have a variable of type Any
we may need to recover its value using a more specific type. Unlike methods such as toString()
, this cast does not transform the stored value, only the type assigned to it. There are two ways to achieve this:
- The unsafe type cast
as
: Conversion is always performed and can cause runtime errors.
var tmp1: String? = null
var tmp2: String? = null
var v1: Any = "hello"
var v2: Any = 12
tmp1 = v1 as String
// Correct: tmp1 <- "hello"
tmp2 = v2 as String
// Runtime error: 12 is a number, not a String
- The secure type cast
as?
: If the conversion cannot be performed, the result will benull
.
tmp1 = v1 as? String
// Correct: tmp1 <- "hello"
tmp2 = v2 as? String
// tmp1 <- null (12 is a number, not a String)
3.1.5. Finding out the type of a variable
The is
operator checks if a Kotlin expression has a given type. The EXP is TYPE
syntax returns true
if EXP
has the type TYPE
, and false
otherwise. EXP !is TYPE
can also be used to reverse the test. We can use this operator within an if
(single-branch conditional) or when
(multi-branch conditional) statement.
For example, let us consider the following code:
var tmp2: String = “hello”
var v:Any = 12
TestType(v)
v = tmp2
TestType(v)
We define TestType
as:
fun TestType(v:Any)
{
when (v) {
is Int -> Log.i("info","I'm Int")
is String -> Log.i("info","I'm String")
is Float -> Log.i("info","I'm Float")
}
}
In this example, the first call will return "I'm Int"
and the second one "I’m String"
.
3.1.6. Not null check
Kotlin variables and expressions can have a null
value. As in any object-oriented language, accessing an object with a null value will cause a runtime error.
Operator ?
(used after the object name) lets us conditionally make method call or access a property. The call or access is only performed if the object is not null.
Var m:Manager? = null
var t1:Element = Element()
m?.Add(t1)
Conversely, we can force the method call using operator !!
:
m!!.Add(t1)
With this syntax, we are forcing the method call even if the value of variable m
is null. In our example this would cause a runtime exception. Thus, before using this operator we need to ensure that the variable is not null.