Java Primer II: Making sense of Java logic, collections, and inheritance
Contents
- Logic and Comparison
- If-Else structures
- For and While Loops
- Data Structures for Collections: Arrays & ArrayLists
- Inheritance: Subclasses and Superclasses
All the videos in this tutorial are also available in a YouTube playlist.
Logic
We often want to make comparisons and test conditions in Java code. For example, Google Maps will check if GPS is enabled when we fire up the app and ask us if we want to enable GPS if it is not enabled. To conduct a test like this we need logic. Also, we need a way of controlling which instructions or bits of code get executed depending on some condition.
Checking equality
Java has some special syntax for logic. To check equality between two things we use the double equals (==) operator. For example we can check if the contents of a variable are equal to the number 99:
bottlesOfBeer == 99
We can also do the opposite. != checks if two values are NOT equal (the ! means NOT). To check if a variable is not equal to 0 write:
bottlesOfBeer != 0
Making comparisons
To make comparisons, we can use the less than (<) or greater than (>) signs and even combine those with the equals sign (=). To check: “is myVariable less than two?” write:
myVariable < 2
To check: “is myVariable greater or equal to five?” write:
myVariable >= 5
The If-Else Structure
Often we want to control the kind of instructions to be executed depending on some condition, like the examples above. This kind of behaviour can be achieved with an If-Else clause, which has the following structure:
if ( condition ) {
statement_1;
} else {
statement_2;
}
If the condition is true, then statement_1 will be executed. If the condition is false, then statement_2 will be executed. The condition will be a boolean expression, which evaluates to either true or false. Therefore either statement_1 or statement_2 will be executed, but never both.
Remember how boolean can also by a type of variable? A boolean variable can only hold the value true or false.
Suppose the method checkWeather() returns either true or false depending on the meteorological conditions. We can store this value in a boolean variable called isItSunny.
boolean isItSunny;
isItSunny = checkWeather(); // isItSunny will either be true or false
We can then write:
if (isItSunny) {
goToPark();
frolic();
} else {
goToPub();
}
Sadly, in London the first block of code would rarely get executed.
The logical AND
We can also check if more than one condition holds true. If we want more than one requirement to be satisfied, we can use the double ampersand (&&). For example, we can write “is myVariable greater than 2 AND is myVariable less than 5″:
myVariable > 2 && myVariable < 5
The line above will only evaluate to true if myVariable is equal to 3 or 4. Both conditions have to be true for the whole thing to be true.
The logical OR
If we only need one condition to hold true we can use the logical OR, denoted by ||.
boolean sunny = true;
boolean hangover = false;
if (sunny || hangover) {
goToPark();
frolic();
} else {
goToPub();
}
Here goToPark() and frolic() will be executed since true || false evaluates to true.
Loops – Doing something repeatedly
When something has to be done over and over again, we don’t copy-paste our code. Real programmers use loops. Two common types of loops are while loops and for loops.
While loops
We use while loops when we want to execute a block of code while a certain condition holds true. The structure of a while loop is:
while ( condition ) {
statement;
}
For example, we can have the following count-down timer:
int time = 10;
while ( time > 0 ) {
time = time – 1;
playOminousSoundEffect();
}
rocketLiftOff();
The time variable starts with value 10. The condition, time > 0 evaluates as true, so we enter the while loop. Next the time variable is set to 9 (since 9 = 10 – 1) and we hear a countdown sound. The loop then goes back to evaluating the condition again. The loop executes a second time since 9 > 0 is true. The loop continues to check the condition and execute until time is equal to 0 at which point we exit the loop and rocketLiftOff() is executed.
For loops
The for loop is used when we want to execute a set of statements a predetermined number of times. The structure of a for loop is:
for (initialisation; continuation condition; increment) {
statement; // Body of the loop
}
Similar to the while loop, before each round, the condition is checked before the body of the loop is executed. Following the execution of the body, the increment expression is invoked (i.e., after each iteration of the loop). Here is a sample for loop on how to get through next week:
for (int i = 0; i < 5; i++) {
wakeUp();
goToWork();
sleep();
}
programAndroidOnWeekend();
Here we are initialising an integer variable i with the value 0. Since 0 < 5 evaluates as true, the statements in the loop body are executed. Next, variable i is incremented by one. The expression i++ is equivalent to i = i + 1; (now you know why the naming of programming language C++ signifies some tongue-in-cheek one-one-upmanship regarding its predecessor, the C language). The above loop will be executed 5 times for i = 0, i = 1, i = 2, i = 3, and i = 4. And just like that you have become the master of loops.
Data Structures for Collections
Often we deal with collections or groups of items, like a list of some sort. This is where we’ll want to use a data structure that represents the entire list. Arrays and ArrayLists will be our new best friends.
Arrays
Think of an array like a column in an Excel spreadsheet with a fixed length. Each cell in the column has an index or a “row number” associated with it. Here is how we declare an array of integers:
int[ ] bunchOfIntegers;
or an array of Donut objects:
Donut[ ] myBoxOfDonuts;
This follows the pattern of DataType[ ] variableName; Notice the square brackets [ ]. They tell Java we are dealing with an array.
Next, we have to tell Java how big we want our array to be (i.e., how many “cells” we want). We want to make space for 5 Donut objects in our array:
myBoxOfDonuts = new Donut[5];
Of course the cool kids will want to do assignment and declaration in one go.
DataType[ ] variableName = new DataType[ArraySize];
Accessing an element in an array
However, so far we’ve not actually put any Donut objects into our array. We’ll populate our empty myBoxOfDonuts referring to the “cells” by their index. The reason for countless programming bugs is that people forget that the first element of an array starts at index value 0.
myBoxOfDonuts[0] = PlainDonut; // The first element
myBoxOfDonuts[1] = ChocolateDonut;
myBoxOfDonuts[2] = ToffeeGlazeDonut;
myBoxOfDonuts[3] = SprinklesDonut;
myBoxOfDonuts[4] = RatPoisonDonut;
myBoxOfDonuts[5] = // @)^£&!*# Out of bounds. Don’t do this. We only have size 5.
Similarly when we want to retrieve the value of a particular element in our array, we refer to it by its index.
Donut donutForMyBoss = myBoxOfDonuts[3];
At this point you can probably already see how loops and arrays make great buddies in many a situation, because we can iterate through the elements in an array.
ArrayLists
As awesome as arrays are, there is one slight issue. And by slight, I mean major. Arrays can’t grow or shrink in size. As such, we will never be able to put more than 5 Donuts in our box because we can’t just add a sixth slot. Luckily, someone clever working at Oracle wrote a class to get around this annoyance: the ArrayList.
Here is the syntax of how we create an ArrayList:
ArrayList<Type> variableName = new ArrayList<>();
Notice that we specify the type of objects we’re going to put inside the list within the < > angle brackets. In contrast to arrays we don’t have to specify the size of the ArrayList in advance. Here’s an example of an ArrayList:
ArrayList<String> madDictators = new ArrayList<>();
We can grow the list by adding items to the ArrayList by calling the add(…) method of the ArrayList class.
madDictators.add(“Muammar Gaddafi”);
madDictators.add(“Robert Mugabe”);
madDictators.add(“Saparmurat Niyazov”);
We can fetch items from the ArrayList by passing in the index to the get(…) method.
String firstElement = madDictators.get(0); // returns the first element we added: “Muammar Gaddafi”
String secondElement = madDictators.get(1); // returns “Robert Mugabe”
The ArrayList class has methods to remove items, get the length of the list, clear the list, and a whole host of other methods, which Oracle conveniently lists out on their documentation page:
http://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html
Inheritance – Subclasses and Superclasses
Inheritance is a fundamental concept of Java and object-oriented programming. The idea behind inheritance is to create new classes that are built on existing classes. The new class will inherit its parent’s methods and fields, but can be further customised to fit our needs. This means that when we make an Android app, we don’t actually have to write all our components from scratch. Instead we use the classes written by Google and customise these to fit our needs.
Let’s go back to the Car class example from the first primer:
public class Car {
private int maxSpeed;
private int nrOfSeats;
public Car (int speed, int seats) {
maxSpeed = speed;
nrOfSeats = seats;
}
public void drive() {
System.out.println(“Cruisin in my Pinto, I see homies as I pass …”);
}
}
Certain properties like maximum speed and number of seats are in common with all cars. However, we may need a car that does something specific like run on electricity or impress a gold digger. If we were to change the Car class to run on electricity, it will affect all the Car objects, which is not what we want.
Extending an existing class
An object-oriented solution to this problem is to have different blueprints for different types of cars – say, an ElectricCar class and a SportsCar class.
public class ElectricCar extends Car {
…
// An ElectricCar can do everything a Car can do, but in addition it can do this:
public void runOnElectricity() {
// fancy code here
}
}
The keyword extends denotes that ElectricCar is a child or subclass of Car, so it will still have maxSpeed and nrOfSeats fields as well as drive() method in addition to the runOnElectricity() method, which we just added.
ElecticCar Tesla = new ElectricCar(200, 4);
Tesla.runOnElectricity();
Superclass methods
The subclass can call the methods of its parent, its superclass. It can call the methods of its parent explicitly using the keyword super. In addition, a subclass can also redefine a method from the parent using the @Override annotation. For example, our SportsCar will drive a little differently than a standard Car object:
public class SportsCar extends Car {
…
@Override // Redefining a method inherited from the parent
public void drive() {
super.drive(); // Calling a method of the parent class
impressGoldDigger();
}
}
Here we are overriding the Car class’ drive() method to expand on it with an extra method called impressGoldDigger(). Since we are redefining an existing method, we have to let the computer know what we are doing with the @Override annotation. Second we are calling super.drive() which executes the Car’s drive() method. Subsequently we execute our SportsCar specific behaviour with impressGoldDigger().
Congrats. We’ve made it through the boring theory bit. Now that we’ve got those key Java concepts down, let’s program some Android apps!