Basic values in Kotlin
This is a chapter from the book Kotlin Essentials. You can find it on LeanPub or Amazon. It is also available as a course.
Every language needs a convenient way to represent basic kinds of values, like numbers or characters. All languages need to have built-in types and literals. Types are used to represent certain types of values. Some type examples are Int
, Boolean
, or String
. Literals are built-in notations that are used to create instances. Some literal examples are a string literal, which is text in quotation marks, or an integer literal, which is a bare number.
In this chapter, we’ll learn about the basic Kotlin types and their literals:
- numbers (
Int
,Long
,Double
,Float
,Short
,Byte
), - booleans (
Boolean
), - characters (
Char
), - strings (
String
).
There is also the array primitive type in Kotlin, which will be covered in the chapter Collections.
In Kotlin, all values are considered objects (there are no primitive types), so they all have methods, and their types can be used as generic type arguments (this will be covered later). Types that represent numbers, booleans, and characters might be optimized by the Kotlin compiler and used as primitives, but this optimization does not affect Kotlin developers, therefore you don’t need to even think about it.
Let's start discussing the basic types in Kotlin, one by one.
Numbers
In Kotlin, there is a range of different types that are used to represent numbers. They can be divided into those representing integer numbers (without decimal points) and those representing floating-point numbers (with decimal points). In these groups, the difference is in the number of bits used to represent these numbers, which determines the possible number size and precision.
To represent integer numbers, we use Int
, Long
, Byte
, and Short
.
Type | Size (bits) | Min value | Max value |
---|---|---|---|
Byte | 8 | -128 | 127 |
Short | 16 | -32768 | 32767 |
Int | 32 | ||
Long | 64 |
To represent floating-point numbers, we use Float
and Double
.
Type | Size (bits) | Significant bits | Exponent bits | Decimal digits |
---|---|---|---|---|
Float | 32 | 24 | 8 | 6-7 |
Double | 64 | 53 | 11 | 15-16 |
A plain number without a decimal point is interpreted as an Int
. A plain number with a decimal point is interpreted as a Double
.
You can create Long
by using the L
suffix after the number. Long
is also used for number literals that are too big for Int
.
Similarly, you can create a Float
by ending a number with the F
or f
suffix.
There is no suffix to create Byte
or Short
types. However, a number explicitly typed as one of these types will create an instance of this type. This also works for Long
.
This is not a conversion! Kotlin does not support implicit type conversion, so you cannot use Byte
or Long
where Int
is expected.
If we need to explicitly convert one number to another type, we use explicit conversion functions like toInt
or toLong
.
Underscores in numbers
In number literals, we can use the underscore _
between digits. This character is ignored, but we sometimes use it to format long numbers for better readability.
Other numeral systems
To define a number using the hexadecimal numeral system, start it with 0x
. To define a number using the binary numeral system, start it with 0b
. The octal numeral system is not supported.
Number
and conversion functions
All basic types that represent numbers are a subtype of the Number
type.
The Number
type specifies transformation functions: from the current number to any other basic type representing a number.
This means that for each basic number you can transform it into a different basic number using the to{new type}
function. Such functions are known as conversion functions.
Operations on numbers
Numbers in Kotlin support the basic mathematical operations:
- addition (
+
), - subtraction (
-
), - multiplication (
*
), - division (
/
).
Notice, that the correct result of
1.4 / 2.5
should be0.56
, not0.5599999999999999
. This problem will be addressed soon.
Beware that when we divide an Int
by an Int
, the result is also Int
, so the decimal part is lost.
The solution is first to convert an integer into a floating-point representation and then divide it.
There is also a remainder operator2 %
:
Kotlin also supports operations that modify a read-write variable var
:
+=
, wherea += b
is the equivalent ofa = a + b
,-=
, wherea -= b
is the equivalent ofa = a - b
,*=
, wherea *= b
is the equivalent ofa = a * b
,/=
, wherea /= b
is the equivalent ofa = a / b
,%=
, wherea %= b
is the equivalent ofa = a % b
,- post-incrementation and pre-incrementation
++
, which increment variables value by1
, - post-decrementation and pre-decrementation
--
, which decrement variables value by1
.
Operations on bits
Kotlin also supports operations on bits using the following methods, which can be called using the infix notation (so, between two values):
and
keeps only bits that have1
in the same binary positions in both numbers.or
keeps only bits that have1
in the same binary positions in one or both numbers.xor
keeps only bits that have exactly one1
in the same binary positions in both numbers.shl
shifts the left value left by the right number of bits.shr
shifts the left value right by the right number of bits, filling the leftmost bits with copies of the sign bit.ushr
shifts the left value right by the right number of bits, filling the leftmost bits with zeros.
BigDecimal
and BigInteger
All basic types in Kotlin have limited size and precision, which can lead to imprecise or incorrect results in some situations.
This is a standard tradeoff in programming, and in most cases we just need to accept it. However, there are cases where we need to have perfect precision and unlimited number size. On JVM, for unlimited number size we should use BigInteger
, which represents a number without a decimal part. For unlimited size and precision, we should use the BigDecimal
, which represents a number that has a decimal part. Both can be created using constructors1, factory functions (like valueOf
), or a conversion from basic types that represent numbers (toBigDecimal
and toBigInteger
methods).
BigDecimal
and BigInteger
also support basic mathematical operators:
On platforms other than Kotlin/JVM, external libraries are needed to represent numbers with unlimited size and precision.
Booleans
Another basic type is Boolean
, which has two possible values: true
and false
.
We use booleans to express yes/no answers, like:
- Is the user an admin?
- Has the user accepted the cookies policy?
- Are two numbers identical?
In practice, booleans are often a result of some kind of comparison.
Equality
A Boolean
is often a result of equality comparison. In Kotlin, we compare two objects for equality using the double equality sign ==
. To check if two objects are not equal, we use the non-equality sign !=
.
Numbers and all objects that are comparable (i.e., they implement the Comparable
interface) can also be compared with >
, <
, >=
, and <=
.
Boolean operations
There are three basic logical operators in Kotlin:
- and
&&
, which returnstrue
when the value on both its sides istrue
; otherwise, it returnsfalse
. - or
||
, which returnstrue
when the value on either of its sides istrue
; otherwise, it returnsfalse
. - not
!
, which turnstrue
intofalse
, andfalse
intotrue
.
Kotlin does not support any kind of automatic conversion to Boolean
(or any other type), so logical operators should be used only with objects of type Boolean
.
Characters
To represent a single character, we use the Char
type. We specify a character using apostrophes.
Each character is represented as a Unicode number. To find out the Unicode of a character, use the code
property.
Kotlin accepts Unicode characters. To describe them by their code, we start with \u
, and then we need to use hexadecimal format, just like in Java.
Strings
Strings are just sequences of characters that form a piece of text. In Kotlin, we create a string using quotation marks "
or triple quotation marks """
.
A string wrapped in single quotation marks requires text in a single line. If we want to define a newline character, we need to use a special character \n
. This is not the only thing that needs (or might need) a backslash to be expressed in a string.
Escape Sequence | Meaning |
---|---|
\t | Tab |
\b | Backspace |
\r | Carriage return |
\f | Form feed |
\n | Newline |
\' | Single quotation mark |
\" | Quotation mark |
\\ | Backslash |
\$ | Dollar |
Strings in triple quotation marks can be multiline; in these strings, special characters can be used directly, and forms prefixed by a backslash don’t work.
To better format triple quotation mark strings, we use the trimIndent
function, which ignores a constant number of spaces for each line.
String literals may contain template expressions, which are pieces of code that are evaluated and whose results are concatenated into a string. A template expression starts with a dollar sign ($
) and consists of either a variable name (like "text is $text"
) or an expression in curly braces (like "1 + 2 = ${1 + 2}"
).
If you need to use a special character inside a triple quotation mark string, the easiest way is to specify it with a regular string and include it using template syntax.
In Kotlin strings, we use Unicode, so we can also define a Unicode character using a number that starts with \u
, and then specifying a Unicode character code in hexadecimal syntax.
You can use +
operator to concatenate two strings, so to create a new string that is a combination of two other strings. You can also use this operator to concatenate a string with any other object, which will be converted to a string. The result is always a string.
Summary
In this chapter, we've learned about the basic Kotlin types and the literals we use to create them:
- Numbers that are represented by types
Int
,Long
,Double
,Float
,Short
, andByte
are created with bare number values with possible suffixes for type customization. We can define negative numbers or decimal parts. We can also use underscores for nicer number formatting. - Boolean values
true
andfalse
are represented by theBoolean
type. - Characters, which are represented by the
Char
type. We define a character value using single quotation marks. - Strings, which are used to represent text, are represented by the
String
type. Each string is just a series of characters. We define strings inside double quotation marks.
So, we have the foundations for using Kotlin. Let's move on to more-complicated control structures that determine how our code behaves.
Constructors will be covered in the chapter Classes.
This operator is similar to modulo. Both the remainder and the modulo operations act the same for positive numbers but differently for negative numbers. The result of -5 remainder 4 is -1 because -5 = 4 * (-1) + (-1). The result of -5 modulo 4 is 3 because -5 = 4 * (-2) + 3.