Lesson 3 - More on the Kotlin type system: Data types
In the previous exercise, Solved tasks for Kotlin lessons 1-2, we've practiced our knowledge from previous lessons.
Lesson highlights
Are you looking for a quick reference on Kotlin data types instead of a thorough-full lesson? Here it is:
Creating variables of basic whole-number data types:
{KOTLIN_CONSOLE}
var a: Byte = 15 // can store numbers from -128 to 127
var b: Short = 10000 // -32 768 to 32 767
var c: Int = 500000 // -2 147 483 648 to 2 147 483 647
var d: Long = 10000000000 // -9 223 372 036 854 775 808 to 9 223 372 036 854 775 807
println(a + b) // We can use basic arithmetics
{/KOTLIN_CONSOLE}
Creating decimal variables:
{KOTLIN_CONSOLE}
var f: Float = 3.141592f // single precision
var d: Double = 3.14159265358979 // double precision
println(f)
println(d)
{/KOTLIN_CONSOLE}
Declaring other built-in data types:
{KOTLIN_CONSOLE}
var s: String = "This text can be as long as we want"
var a: Char = 'A' // One character
var loveKotlin: Boolean = true // booleans are true or false
println(s)
println(a)
println(loveKotlin)
{/KOTLIN_CONSOLE}
Calling String
methods
(startsWith/endsWith/contains/toUpperCase/toLowerCase/replace):
{KOTLIN_CONSOLE}
val s = "Think twice, code once "
println(s.startsWith("Think"))
println(s.endsWith("once"))
println(s.trim().endsWith("once"))
println(s.contains("TWICE"))
println(s.toUpperCase())
println(s.toLowerCase())
println(s.replace("Think", "Learn"))
println("My favorite quote is: $s with ${s.length} characters")
{/KOTLIN_CONSOLE}
Would you like to learn more? A complete lesson on this topic follows.
In the previous lesson, Solved tasks for Kotlin lessons 1-2, we learned basic data types of Kotlin.
These were Int
, Double
, and String
. In
today's tutorial, we're going to look at them in more detail and explain how to
use them correctly. Today is going to be more theoretical, and the next lesson
will be very practical. At the end, we'll make a few simple examples.
Kotlin provides a lot of data types for storing different data. Java and other programming languages recognize whether a type is primitive or complex and then work with it differently in order to optimize performance. Although this mechanism saved some time, it wasn't intuitive. Kotlin doesn't make such a distinction, and ordinary numbers are internally stored as same as complex objects.
Whole-number data types
Let's look at the table of all of the built-in whole-number data types in
Kotlin, notice the type Int
, which we already know.
Data type | Range | Size |
---|---|---|
Byte | -128 to 127 | 8 bits |
Short | -32 768 to 32 767 | 16 bits |
Int | -2 147 483 648 to 2 147 483 647 | 32 bits |
Long | -9 223 372 036 854 775 808 to 9 223 372 036 854 775 807 | 64 bits |
Of course, you don't have to memorize all these types, you can use just
Int
to store whole numbers.
By now, you might be thinking - why do we have so many data types for storing
numbers? The answer is simple, it depends on their size. If the number is large,
it consumes more memory. For user's age, we should select Byte
since nobody can live more than 127
years. Imagine a database with
millions of users of some informational system. If we choose Int
instead of Byte
, it'll occupy 4 times more space. Conversely, if we
have a function that calculates a factorial, the range of Int
will
not be enough for us and we'll have to use Long
.
We don't have to think hard about choosing data types, we'll use
Int
almost every time. You should think about it only in case the
variables are in some array or collection in general, and there are a lot of
them. In that case, it's worth it to consider memory requirements. The tables I
gave here are mainly for the sake of completeness.
Decimal numbers
For decimal numbers, the choice is simpler, we can only choose between two
data types. They differ in the range of values, and also in precision, i.e. in
the number of decimal places. The datatype Double
is twice as
precise as Float
, which you probably deduced from its name.
Data type | Range | Precision |
---|---|---|
Float | +-1.5 * 10−45 to +-3.4 * 1038 | 7 numbers |
Double | +-5.0 * 10−324 to +-1.7 * 10308 | 15-16 number |
Due to the fact that decimal numbers are stored in your computer in a binary system, there is some precision loss. Although the deviation is almost negligible, if you're programming, e.g. a financial system, don't use these data types for storing money since it could lead to slight deviations.
When we want to assign a value to a Float
variable in the source
code, we have to use the f
suffix. With Double
, we
don't have to use any since Double
is the default decimal type:
val f: Float = 3.14f val d = 2.72
As the decimal separator in source code, we use dots, regardless of our OS regional settings.
Other built-in data types
Let's look at the other data types that Kotlin offers:
Data type | Range | Size/Precision |
---|---|---|
Char | U+0000 to U+ffff | 16 bits |
Boolean | true or false | 8 bits |
Char
Char
represents one character, unlike String
, which
represents the entire string of Char
s. We declare characters with
apostrophes in Kotlin:
val c: Char = 'A'
A single, non-array, char
actually belongs in the list of
whole-number variables. It contains a numeric character code, but it seemed more
logical for me to introduce it here.
Boolean
The Boolean
data type has two possible values:
true
false
We'll use it especially when we get to the conditions. In a variable of the
Boolean
type, we can store either
true
/false
or a logical expression. Let's try a simple
example:
val b = false val expression = 15 > 5 println(b) println(expression)
The program output:
false true
We may enclose expressions in parentheses. That may be useful when we have
more of them. Notice that the expression is equal to true
since
15
is in fact more than 5
. Going from expressions to
conditions isn't a far stretch, but we'll go into them in the next lesson.
String
The data type that you will see on every corner. Represents a string of characters, just any text. We'll introduce the most important methods that are good to know or at least it's good to know they exist.
contains()
,
endsWith()
a startsWith()
We can ask if a String
starts with, ends with or contains a
substring. A substring is a part of a String
. All of these methods
will take a substring as a parameter and return Boolean
(true
/false
). We can't react to the output yet;
however, let's write the return values nonetheless:
val s = "Rhinopotamus" println(s.startsWith("rhin")) println(s.endsWith("tamus")) println(s.contains("pot")) println(s.contains("lol"))
The program output:
Console application
false
true
true
false
We can see that everything works as expected. The first phrase failed, as expected because the string actually starts with a capital letter.
ToUpperCase()
and
toLowerCase()
Distinguishing between capital and lowercase letters is not always what we
want. We'll often need to ask about the presence of a substring in a
case-insensitive way. The situation can be solved using
toUpperCase()
and toLowerCase()
methods which return
the string in uppercase, resp. lowercase. Let's make a more realistic example
than Rhinopotamus. The variable will contain a line from some configuration
file, which was written by the user. Since we can't rely on the user's input
we'll try to eliminate possible errors, here by ignoring letter cases.
var config = "Fullscreen shaDows autosave" config = config.toLowerCase() println("Will the game run in fullscreen?") println(config.contains("fullscreen")) println("Will shadows be turned on?") println(config.contains("shadows")) println("Will sound be turned off?") println(config.contains("nosound")) println("Would the player like to use autosave?") println(config.contains("autosave"))
The program output:
Console application
Will the game run in fullscreen?
true
Will shadows be turned on?
true
Will sound be turned off?
false
Would the player like to use autosave?
true
We can see that we're able to detect the presence of particular words in a string. First, we convert the entire string to lowercase or uppercase, and then check the presence of the word in lowercase or uppercase, respectively. By the way, simple processing of configuration script may actually look like this.
replace()
Probably the most important method on String
is a replacement of
its parts with another text. We enter two substrings as parameters, the first
one is the one want to be replaced and the second one will replace it. The
method returns a new String
in which the replacing occurred. When
the method doesn't find the substring, it returns the original string. Let's
try:
var s = "Java is the best!" s = s.replace("Java", "Kotlin") println(s)
We'll get:
Console application
Kotlin is the best!
String templates
We've already mentioned string templates, sometimes referred to as string
interpolation. In Kotlin, if we want to insert some variables into a string at
certain places, we write them directly to the String using the $
characters.
val a = 10 val b = 20 val c = a + b val s = "When we add $a and $b, we get $c"
The program's output:
When we add 10 and 20, we get 30
We enclose complex expressions in braces after the dollar sign as
${a + b}
. For example, if we want to add two numbers while
printing. If we think about it, there is no other way how Kotlin would
understand we want to add 2 variables if we wrote a dollar before the first one
only.
This functionality if often useful for printing outputs. Let's try to add the numbers right in the expression:
val a = 10 val b = 20 println("When we add $a and $b, we get ${a + b}")
This is a very useful and easy way to build strings. If we want to print
numbers, we can't avoid it. For combining variables containing strings with
another strings, we can use a concatenation (simply use +
to merge
them into a single string).
val firstName = "Bill" val lastName = "Gates" val fullName = firstName + " " + lastName
The length
property
The last but most important property (NOT a method) is length
,
i.e. the number of characters. It returns an integer that represents the number
of characters in the string. We don't use parentheses because properties don't
have parameters.
println("Type in your name:") val name = readLine()!! println("Your name is ${name.length} characters long.")
There's still a lot to go over and lots of other data types that we haven't covered. Regardless, there is a time for everything. In the next lesson, Solved tasks for Kotlin lesson 3, we'll introduce conditions and then loops, then we'll have enough knowledge to create interesting programs
In the following exercise, Solved tasks for Kotlin lesson 3, we're gonna practice our knowledge from previous lessons.