Variables
We can think of a variable as a named storage box. We choose a name, perhaps variableA
. These names are the programmer's access into the memory of the user's Android device.
Variables are values in memory that are ready to be used when necessary by referring to them with their name.
Computer memory has a highly complex system of addresses that, fortunately, we do not need to directly interact with. Kotlin variables allow us to devise our own convenient names for all the data that we need our app to work with. The operating system will, in turn, interact with the physical (hardware) memory.
So, we can think of our Android device's memory as a huge warehouse waiting for us to add our variables. When we assign names to our variables, they are stored in the warehouse, ready for when we need them. When we use our variable's name, the device knows exactly what we are referring to. We can then tell it to do things, such as the following:
- Assign a value to
variableA
- Add
variableA
tovariableB
- Test the value of
variableB
and take an action based on the result
In a typical app, we might have a variable named unreadMessages
; perhaps to hold the number of unread messages that the user has. We can add to it when a new message arrives, take away from it when the user reads a message, and show it to the user somewhere in the app's layout, so that they know how many unread messages they have.
Situations that might arise can include the following:
- The user gets three new messages, so add three to the value of
unreadMessages
. - The user logs into the app, so use
Toast
to display a message along with the value stored inunreadMessages
. - The user sees that a couple of the messages are from someone they don't like and deletes two messages. We could then subtract two from
unreadMessages
.
Variable names are arbitrary, and if you don't use any of the characters or keywords that Kotlin restricts, you can call your variables whatever you like.
In practice, however, it is best to adopt a naming convention so that your variable names will be consistent. In this book, we will use a simple convention of variable names starting with a lowercase letter. When there is more than one word in the variable's name, the second word will begin with an uppercase letter. This is called camel casing.
Here are some examples of camel-case variable names:
unreadMessages
contactName
isFriend
Before we take a look at some real Kotlin code that uses some variables, we need to first look at the types of variables that we can create and use.
Types of variables
It is not hard to imagine that even a simple app will have quite a few variables. In the previous section, we introduced the unreadMessages
variable as a hypothetical example. What if the app has a list of contacts and needs to remember each of their names? Then, we might need variables for each contact.
And what about when an app needs to know whether a contact is also a friend, or just a regular contact? We might need code that tests for friend status and then adds messages from that contact into an appropriate folder, so that the user knows whether they were messages from a friend or not.
Another common requirement in a computer program, including Android apps, is the right or wrong test. Computer programs represent right or wrong calculations using true and false.
To cover these and many other types of data that you might want to store or manipulate, Kotlin uses variables of different types.
There are many types of variables, and we can even invent our own types as well. But, for now, we will look at the most commonly-used Kotlin types, and these will cover just about every situation that we are likely to run into. The best way to explain types is through a number of examples.
We have already discussed the hypothetical unreadMessages
variable. This variable is, of course, a number.
On the other hand, the hypothetical contactName
variable will hold the characters or letters that make up a contact's name.
The type that holds a regular number is called an Int (an abbreviation of integer) type, and the type that holds name-like data is called a String.
Here is a list of the types of variables that we will use in this book:
Int
: TheInt
type is for storing integers and whole numbers. This type can store values with a size that is in excess of 2 billion, including negative values too.Long
: As the name suggests,Long
data types can be used when even larger numbers are needed. ALong
variable can store numbers up to 9,223,372,036,854,775,807. That's a lot of unread messages. There are plenty of uses forLong
variables, but if a smaller variable will do, we should use it because our app will use less memory.Float
: This variable is used for floating point numbers. That is, numbers where there is precision beyond the decimal point. As the fractional part of a number takes memory space just as the whole number part, the range of a number that is possible in aFloat
variable is, therefore, decreased compared to non-floating-point numbers. So, unless our variable will use the extra precision,Float
will not be our data type of choice.Double
: When the precision in aFloat
variable is not enough, we haveDouble
.Boolean
: We will be using plenty of Booleans throughout the book. TheBoolean
variable type can be eithertrue
orfalse
; nothing else. Booleans answer questions, such as the following:- Is the contact a friend?
- Are there any new messages?
- Are two examples of Booleans enough?
Char
: This stores a single alphanumeric character. It's not going to change the world on its own, but it could be useful if we put lots of them together.String
: Strings can be used to store any keyboard character. It is similar to aChar
variable but of almost any length. Anything from a contact's name to an entire book can be stored in a singleString
. We will be using Strings regularly, including in this chapter.Class
: This is the most powerful data type and we have already discussed it a little. We will take a deep dive into classes in Chapter 10, Object-Oriented Programming.Array
: This type comes in lots of different flavors and is key for handling and organizing large sets of data. We will explore the variations ofArray
in Chapter 15, Handling Data and Generating Random Numbers.
Now we know what variables are and that there is a wide selection of types, we are nearly ready to see some actual Kotlin code.
Declaring and initializing variables
Before we can use a variable type that we just discussed, we must declare them, so that the compiler knows they exist, and we must also initialize them, so they hold a value.
For each of the variable types in Kotlin, such as Int
, Float
, and String
, there are two keywords that we can use to declare them: val
and var
.
The val
type is for storing values that are decided by the programmer before the application starts or during initialization and cannot be changed again during execution. The var
type is for values that can be manipulated and altered throughout execution.
Therefore, the val
type is only readable. In technical terms, it is known as immutable. The var
type is readable and writeable, and this is called mutable. Writing code that attempts to change the value of a val
type during execution will cause Android Studio to show an error and the code will not compile. There are also rules for var
that we will explore later.
There are the two ways that we can declare and initialize a String
type; first, by using val
, as follows:
val contactName: String = "Gordon Freeman"
In the preceding line of code, a new val
variable named contactName
and of type String
is declared and now holds the Gordon Freeman
value.
Furthermore, the Gordon Freeman
text is now the only value that contactName
can hold for the duration of the app's execution. You could attempt to change it with the following line of code:
contactName = "Apple Crumble" // Causes an error
Here is what you will see if you paste the preceding code into the onCreate
function in an Android project:
Android Studio is helping us to enforce our decision to make the variable constant. Of course, we will frequently need to change the value held by a variable. When we do, we will use var
instead; take a look at the next two lines of code:
var contactName: String = "Gordon Freeman" contactName = "Alyx Vance" // No problem
In the preceding code, we use var
to declare a String
type, and this time we successfully change the value held by contactName
to Alyx Vance
.
The point to take away here is that if the variable does not need to change during the execution of the app, then we should use val
, because the compiler can help to protect us from making mistakes.
Let's declare and initialize some different types of variables:
val battleOfHastings: Int = 1066 val pi: Float = 3.14f var worldRecord100m: Float = 9.63f var millisecondsSince1970: Long = 1544693462311 // True at 9:30am 13/12/2018 val beerIsTasty: Boolean = true var isItRaining: Boolean = false val appName: String = "Express Yourself" var contactName: String = "Geralt" // All the var variables can be reassigned worldRecord100m = 9.58f millisecondsSince1970 = 1544694713072 // True at 9:51am 13/12/2018 contactName = "Vesemir"
Notice that in the previous code, I declared variables as val
when they are unlikely to change and as var
when it is likely that they will change. As you develop your app, you can guess whether to use val
or var
and, if necessary, you can change a var
variable to a val
variable, or the other way around. Also, in the preceding code notice that String
types are initialized with the value between speech marks but Int
, Float
, Long
, and Boolean
are not.
Saving keystrokes with type inference
Kotlin was designed to be as succinct as possible. It was one of the aims of the JetBrains team to let developers get as much done with as little code as possible. We will see examples of this throughout the Kotlin language. If you have previously coded in another language, especially Java, you will notice a significant reduction in typing. The first example of this is type inference.
Kotlin can often infer the type you need from the context, and if this is the case, then you don't need to write the type explicitly; consider the following example:
var contactName: String = "Xian Mei"
In the preceding code, a String
type called contactName
is declared and initialized using "Xian Mei". If you think about it for a moment, it must be a String
. Fortunately, this is obvious to the Kotlin compiler too. We could (and should) improve the preceding line of code using type inference, such as with this next code:
var contactName = "Xian Mei"
The colon and the type have been omitted, but the result is identical.
Tip
Java programmers will also notice that Kotlin code does not need to have a semicolon at the end of each line. If you like semicolons, however, the compiler will not complain if you do add one to the end of each line:
var contactName = "Xian Mei"; // OK but superfluous
We must remember, however, that although we haven't specified String
explicitly, it is still a String
type – and only a String
type. If we try to do something unsuitable for a String
type, then we will get an error; for example, as we do when we try to reinitialize it to a number value as in this code:
contactName = 3.14f // Error
The preceding code will be flagged in Android Studio and compilation won't work. Here are all the declarations and initializations from the previous section of code, but this time using type inference:
val battleOfHastings = 1066 val pi = 3.14f var worldRecord100m = 9.63f var millisecondsSince1970 = 1544693462311 // True at 9:30am 13/12/2018 val beerIsTasty = true var isItRaining = false val appName = "Express Yourself" var contactName = "Geralt"
We will see more type inference with variables in the next two sections, and in later chapters, we will use type inference with more complex types such as classes, Arrays, and Collections. Type inference will also become a good timesaver, by making our code shorter and more manageable.
It might sound obvious, but it is worth mentioning that if you are declaring a variable for initialization later then type inference is not possible, as shown in the following code:
var widgetCount // Error
The preceding line of code will cause an error and the app will not compile.
When using type inference, it will usually be obvious what type a variable is but, if there is ever any doubt, you can select a variable in Android Studio and press Shift + Ctrl + P simultaneously to get a handy onscreen hint:
Omitting the occasional String
, Int
, or colon (:
) type isn't going to change much on its own, so let's learn how to make expressions with our variables by combining them with operators.