Android Programming with Kotlin for Beginners
上QQ阅读APP看书,第一时间看更新

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 to variableB
  • 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 in unreadMessages.
  • 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: The Int 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. A Long 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 for Long 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 a Float 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 a Float variable is not enough, we have Double.
  • Boolean: We will be using plenty of Booleans throughout the book. The Boolean variable type can be either true or false; 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 a Char variable but of almost any length. Anything from a contact's name to an entire book can be stored in a single String. 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 of Array 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.