Android:Game Programming
上QQ阅读APP看书,第一时间看更新

Chapter 6. OOP – Using Other People's Hard Work

OOP stands for object-oriented programming. In this chapter, you don't need to even try and remember everything. Why do I say this? Surely, that's what learning is. The more important thing is to grasp the concepts and begin to understand the why of OOP rather than memorize rules, syntax, and jargon.

The more important thing is to actually start to use some of the concepts, even though you might have to keep referring back and your code might not properly adhere to every OOP principal that we discuss. Neither does the code in this book. The code in this chapter is here to help you explore and grasp the concepts of OOP.

If you try to memorize this chapter, you will have to make a lot of room in your brain, and you will probably forget something really important in its place such as going to work or thanking the author for telling you not to try and memorize this stuff.

A good goal will be to try and almost get it. Then we will start to recognize examples of OOP in action so that our understanding becomes more rounded. You can then often refer back to this chapter for a refresher.

So what is all this OOP stuff we will learn about? Actually, we have already learned loads about OOP. Until now, we have been using classes such as Button, Random, and Activity, overriding methods of classes (mainly onCreate) and using an interface as well; remember implementing onClickListener a few times in the first five chapters?

This chapter just helps to make sense of OOP and expands our understanding, and finally, we will make our own classes.

Then we will be in a good position in the next two chapters to make two cool retro arcade games using lots of other people's hard work. This chapter will be mainly theory, but with a few practical console examples using LogCat so that we can see OOP in action.

In this chapter, we will do the following:

  • Look at what OOP is.
  • Write our first class.
  • Look at what encapsulation is and how we achieve it as well as look more deeply at variables and the different types. We will also take a short break to throw out the garbage.
  • Learn about inheritance and how we can extend and even improve upon a class before we use it.
  • Take a look at polymorphism, which is a way of being more than one thing at a time and is really useful in programming.

What is OOP?

OOP is a way of programming that involves breaking our requirements down into chunks that are more manageable than the whole.

Each chunk is self-contained yet potentially reusable by other programs while working together as a whole with the other chunks.

These chunks are what we have been referring to as objects. When we plan an object, we do so with a class. A class can be thought of as the blueprint of an object.

We implement an object of a class. This is called an instance of a class. Think about a house blueprint. You can't live in it, but you can build a house from it, which means you build an instance of it. However, OOP is more than this. It is also a methodology that defines best practices such as the following:

  • Encapsulation: This means keeping the internal workings of your code safe from interference from the programs that use it, and allowing only the variables and methods you choose to be accessed. This means your code can always be updated, extended, or improved without affecting the programs that use it, as long as the exposed parts are still accessed in the same way.
  • Inheritance: Just like it sounds, inheritance means we can harness all the features and benefits of other people's hard work, including encapsulation and polymorphism, while refining their code specifically for our situation. Actually, we have done this already every time we used the extends keyword.
  • Polymorphism: This allows us to write code that is less dependent on the types we are trying to manipulate, making our code clearer and more efficient. Some examples later in the chapter will make this clear.

Tip

When we talk about using other people's hard work, we are not talking about a magical way to abuse copyright and get away with it. Some code is plain and simple, someone else's property. What we are taking about is the vast array of free-to-use code, particularly in the context of this book, in the Java and Android APIs. If you want some code that does a certain thing, it has probably been done before. We just have to find it, then use it or modify it.

Java was designed from the start with all of this in mind, so we are fairly significantly constrained to using OOP. However, this is a good thing because we learn how to use the best practices.

Why do it like this?

When written properly, all this OOP allows you to add new features without worrying as much about how they interact with existing features. When you do have to change a class, its self-contained nature means less, or perhaps zero, consequences for other parts of the program. This is the encapsulation part.

You can use other people's code without knowing or perhaps even caring how it works. Think about the Android lifecycle, buttons, threads, and so on. The Button class is quite complicated, with nearly 50 methods—do we really want to write all that just for a button?

OOP allows you to write apps for highly complex situations without breaking a sweat. You can create multiple similar yet different versions of a class without starting the class from scratch using inheritance, and you can still use the methods intended for the original type of object with your new object because of polymorphism.

Makes sense, really! Let's write some classes and then make some objects out of them.

Our first class and first object

So what exactly is a class? A class is a bunch of code that can contain methods, variables, loops, and all other types of Java syntax. A class is part of a package and most packages will normally have multiple classes. Usually, but not always, each new class will be defined in its own .java code file with the same name as the class.

Once we have written a class, we can use it to make as many objects from it as we need. Remember, the class is the blueprint, and we make objects based on the blueprint. The house isn't the blueprint just as the object isn't the class; it is an object made from the class.

Here is the code for a class. We call it a class implementation:

public class Soldier {
  int health;
  String soldierType;

  void shootEnemy(){
    //bang bang
  }
  
}

The preceding snippet of code is a class implementation for a class called Soldier. There are two variables, an int variable called health and a string variable called soldierType.

There is also a method called shootEnemy. The method has no parameters and a void return type, but class methods can be of any shape or size that we discussed in Chapter 5, Gaming and Java Essentials.

When we declare variables in a class, they are known as fields. When the class is instantiated into a real object, the fields become variables of the object itself, so we call them instance variables. Whichever fancy name they are referred to by, they are just variables of the class. However, the difference between fields and variables declared in methods (called the local variables) becomes more important as we progress. We will look at all types of variables again in the Variables revisited section.

Remember, this is just a class, not an object. It is a blueprint for a soldier, not an actual soldier object. This is how we make an object of the Soldier type from our Soldier class:

Soldier mySoldier = new Soldier();

In the first part of the code, Soldier mySoldier declares a new reference type variable of type Soldier, called mySoldier, and in the last part of the code, new Soldier() creates an actual Soldier object. Of course, the assignment operator, =, in the middle of the two parts assigns the result of the second part to that of the first. Just like regular variables, we could also have performed the preceding steps like this:

Soldier mySoldier;
mySoldier = new Soldier();

This is how we would assign and use the variables:

mySoldier.health = 100;
mySoldier.soldierType = "sniper";
//Notice that we use the object name mySoldier.
//Not the class name Soldier.
//We didn't do this:
// Soldier.health = 100; ERROR!

In the preceding code snippet, the dot operator, ., is used to access the variables of the class, and this is how we would call the method. Again, we use the object name and not the class name, followed by the dot operator:

mySoldier.shootEnemy();

Tip

As a rough guide, a class's methods are what it can do and its instance variables are what it knows about itself.

We can also go ahead by making another Soldier object and accessing its methods and variables:

Soldier mySoldier2 = new Soldier();
mySoldier2.health = 150;
mySoldier2.soldierType = "special forces";
mySoldier2.shootEnemy();

It is important to realize that mySoldier2 is a totally separate object with totally separate instance variables.

Also notice that everything is done on the object itself. We must create objects of classes in order to make them useful.

Note

As always, there are exceptions to this rule, but they are in the minority, and we will look at the exceptions later in the chapter. In fact, we have already seen an exception way back in Chapter 3, Speaking Java – Your First Game. Think of Toast.

Let's explore basic classes a little more deeply.

Basic classes

What happens when we want an army of Soldier objects? We will instantiate multiple objects. We will also demonstrate the use of the dot operator on variables and methods, and show that different objects have different instance variables.

You can get the working project for this example in the code download. It is in the chapter6 folder and is called simply BasicClasses. Or read on to create your own working example:

  1. Create a project with a blank activity, just as we did in Chapter 2, Getting Started with Android. Clean up the code by deleting the unnecessary parts, but this isn't essential. Call the application BasicClasses.
  2. Now we create a new class called Soldier. Right-click on the com.packtpub.basicclasses folder in the Project Explorer window. Click on New, then on Java Class. In the Name field, type Soldier and click on OK. The new class is created for us, with a code template ready to put our implementation within, just like what is shown in the following screenshot:
  3. Notice that Android Studio has put the class in the same package as the rest of our app. Now we can write its implementation. Write the following class implementation code within the opening and closing curly braces of the Soldier class:
    public class Soldier {
        int health;
        String soldierType;
    
        void shootEnemy(){
            //lets print which type of soldier is shooting
            Log.i(soldierType, " is shooting");
        }
    }
  4. Now that we have a class, a blueprint for our future objects of the Soldier type, we can start to build our army. In the editor window, click on the tab of MainActivity.java. We will write this code, as so often, within the onCreate method just after the call to setContentView:
    //first we make an object of type soldier
       Soldier rambo = new Soldier();
       rambo.soldierType = "Green Beret";
       rambo.health = 150;// It takes a lot to kill Rambo
    
       //Now we make another Soldier object
       Soldier vassily = new Soldier();
       vassily.soldierType = "Sniper";
       vassily.health = 50;//Snipers have less armor
    
       //And one more Soldier object
       Soldier wellington = new Soldier();
       wellington.soldierType = "Sailor";
       wellington.health = 100;//He's tough but no green beret

    Tip

    This is a really good time to start taking advantage of the autocomplete feature in Android Studio. Notice that after you have declared and created a new object, all you have to do is begin typing the object's name and all the autocomplete options will present themselves.

  5. Now that we have our extremely varied and somewhat unlikely army, we can use it and also verify the identity of each object. Type the following code below the code in the previous step:
    Log.i("Rambo's health = ", "" + rambo.health);
    Log.i("Vassily's health = ", "" + vassily.health);
    Log.i("Wellington's health = ", "" + wellington.health);
    
    rambo.shootEnemy();
    vassily.shootEnemy();
    wellington.shootEnemy();
  6. Now we can run our app on an emulator. Remember, all the output will be in the LogCat console window.

This is how the preceding pieces of code work. In step 2, Android Studio created a template for our new Soldier class. In step 3, we implemented our class in quite the same way that we have before—two variables, an int and a string, called health and soldierType, respectively.

We also have a method in our class called shootEnemy. Let's look at it again and examine what is going on:

void shootEnemy(){
        //lets print which type of soldier is shooting
        Log.i(soldierType, " is shooting");
    }

In the body of the method, we print the soldierType string to the console first, and then the arbitrary " is shooting" string. What's neat here is that the soldierType string will be different depending on which object we call the shootEnemy method on.

In step 4, we declared, created, and assigned three new objects of type Soldier. They where rambo, vassily, and wellington. In step 5, we initialized each with a different value for health as well as soldierType.

Here is the output:

Rambo's health =﹕ 150
Vassily's health =﹕ 50
Wellington's health =﹕ 100
Green Beret﹕ is shooting
Sniper﹕ is shooting
Sailor﹕ is shooting

Notice that each time we access the health variable of each Soldier object, it is printed to the value we assigned it, demonstrating that although the three objects are of the same type, they are completely separate individual objects.

Perhaps more interesting are the three calls to shootEnemy. One call by each of our Soldier objects' shootEnemy method is made, and we print the soldierType variable to the console. The method has the appropriate value for each individual object, further demonstrating that we have three distinct objects, albeit created from the same Soldier class.

More things we can do with our first class

We can treat a class much like other variables. Assuming we have already implemented our Soldier class, we can make an array of Soldier objects like this:

//Declare an array called myArmy to hold 10 Soldier objects
Soldier [] myArmy = new Soldier[10];

//Then we can add the Soldier objects
//We use the familiar array notation on the left
//And the newly learnt new Soldier() syntax on the right
myArmy[0] = new Soldier();
myArmy[1] = new Soldier();
myArmy[2] = new Soldier();
myArmy[3] = new Soldier();
//Initialize more here
//..

Then we can use an object from an array using the same style of array notation as we did for regular variables, like this:

myArmy[0].health = 125;
myArmy[0].soldierType = "Pilot";
myArmy[0].shootEnemy();
// Pilot﹕ is shooting

We can also use a class as an argument in a method call. Here is a hypothetical call to a healSoldier method:

healSoldier(rambo);
//Perhaps healSoldier could add to the health instance variable

Tip

Of course, the preceding example might raise questions like should the healSoldier method be a method of a class?

someHospitalObjectPerhaps.healSoldier(rambo);

It could be or not (as shown in the previous example). It would depend upon what is the best solution for the situation. We will look at more OOP, and then the best solution for lots of similar conundrums should present themselves more easily.

As you might have come to expect by now, we can use an object as the return value of a method. Here is what the hypothetical healSoldier method might look like:

Soldier healSoldier(Soldier soldierToBeHealed){
  soldierToBeHealed.health++;

  return soldierToBeHealed;
}

All of this information will likely raise a few questions. OOP is like that, so to try and consolidate all this class stuff with what we already know, let's take another look at variables and encapsulation.

Encapsulation

So far, what we have really seen is what amounts to a kind of code-organizing convention, although we did discuss the wider goals of all this OOP stuff. Now we will take things further and begin to see how we actually manage to achieve encapsulation with OOP.

Tip

Definition of encapsulation

As we have learned encapsulation means keeping the internal workings of your code safe from interference from the programs that use it, allowing only the variables and methods you choose to be accessed. This means your code can always be updated, extended, or improved without affecting the programs that use it, as long as the exposed parts are still made available in the same way. It also allows the code that uses your encapsulated code to be much simpler and easier to maintain because much of the complexity of the task is encapsulated in your code.

But didn't I say that we don't have to know what is going on inside? So you might question what we have seen so far. If we are constantly setting the instance variables like this rambo.health = 100;, isn't it possible that eventually things could start to go wrong, perhaps like the following line of code?

rambo.soldierType = "ballerina";

Encapsulation protects your class from being used in a way that it wasn't meant to be. By strictly controlling the way that your code is used, it can only ever do what you want it to do, with values you can control. It can't be forced into errors or crashes. Also, you are then free to make changes to the way your code works internally, without breaking any programs that are using an older version of the code or the rest of your program:

weighlifter.legstrength = 100;
weighlifter.armstrength = -100;
weightlifter.liftHeavyWeight();
//one typo and weightlifter rips own arms off

We can encapsulate our classes to avoid this, and here is how.

Controlling the use of classes with access modifiers

The designer of the class controls what can be seen and manipulated by any program that uses their class. We can add an access modifier before the class keyword, like this:

public class Soldier{
  //Implementation goes here
}

There are two class access modifiers. Let's briefly look at each in turn:

  • public: This is straightforward. A class declared as public can be seen by all other classes.
  • default: A class has default access when no access modifier is specified. This will make it public but only to classes in the same package, and inaccessible to all others.

Now we can make a start with this encapsulation thing. However, even at a glance, the access modifiers described are not very fine-grained. We seem to be limited to complete lockdown to anything outside the package or a complete free-for-all.

Actually, the benefits here are easily taken advantage of. The idea would be to design a package that fulfills a set of tasks. Then all the complex inner workings of the package, the stuff that shouldn't be messed with by anybody but our package, should have default access (only accessible to classes within the package). We can then provide a careful selection of public classes that can be used by others (or other distinct parts of our program).

Tip

For the size and complexity of the games in this book, multiple packages are almost certainly overkill.

Class access in a nutshell

A well-designed app will probably consist of one or more packages, each containing only default or default and public classes.

In addition to class-level privacy controls, Java gives us very fine-grained controls, but to use these controls, we have to look at variables in more detail.

Controlling the use of variables with access modifiers

To build on class visibility controls, we have variable access modifiers. Here is a variable with the private access modifier being declared:

private int myInt;

Note also that all of our discussion of variable access modifiers applies to object variables too. For example, here is an instance of our Soldier class being declared, created, and assigned. As you can see, the access specified in this case is public:

public Soldier mySoldier = new Soldier(); 

Before you apply a modifier to a variable, you must first consider the class visibility. If class a is not visible to class b, say because class a has default access and class b is in another package, then it doesn't make any difference what access modifiers you use on the variables in class a; class b still can't see it.

Thus, it makes sense to show a class to another class when necessary, but you should only expose the variables that are needed—not everything.

We have a bit more to cover on access modifiers, and then we will look at a few examples to help clarify things. For now, here is an explanation of the different variable access modifiers. They are more numerous and fine-grained than the class access modifiers. Most of the explanations are straightforward, and the ones that might raise questions will become clearer when we look at an example.

The depth and complexity of access modification is not so much in the range of modifiers, but by using them in smart ways, we can combine them to achieve the worthy goals of encapsulation. Here are the variable access modifiers:

  • public: You guessed it! Any class or method from any package can see this variable. Use public only when you are sure that this is what you want.
  • protected: This is the next least restrictive modifier after public. protected Variables set as protected can be seen by any class and any method as long as they are in the same package.
  • default: This doesn't sound as restrictive as protected, but it is more so. A variable has default access when no access is specified. The fact that default is restrictive perhaps implies that we should be thinking on the side of hiding our variables rather than exposing them. At this point, we need to introduce a new concept. Do you remember that we briefly discussed inheritance, and how we can quickly take on the attributes of a class and yet refine it using the extends keyword? Just for the record, default access variables are not visible to subclasses. This means that when we extend a class like we did with Activity, we cannot see its default variables. We will look at inheritance in more detail later in the chapter.
  • private: These variables can only be seen within the class they are declared. Like default access, they cannot be seen by subclasses (inherited classes).

Variable access in a nutshell

A well-designed app will probably consist of one or more packages, each containing only default or default and public classes. Within these classes, variables will have carefully chosen and most likely varied access modifiers.

There's one more twist in all this access modification stuff before we get practical with it.

Methods have access modifiers too

It makes sense that methods are the things that our classes can do. We will want to control what users of our class can and can't do. The general idea here is that some methods will do things internally only and are therefore not needed by users of the class, and some methods will be fundamental to how users use your class.

The access modifiers for methods are the same as those for the class variables. This makes things easy to remember but suggests again that successful encapsulation is a matter of design rather than any specific set of rules.

As an example, the method in the following code snippet, provided in a public class, can be used by any other class:

public useMeEverybody(){
  //do something everyone needs to do here
}

However, the following method can only be used internally by the class that created it:

private secretInternalTask(){
  //do something that helps the class function internally
  //Perhaps, if it is part of the same class,
  //useMeEverybody could use this method...
  //On behalf of the classes outside of this class.
  //Neat!
}

The next method has default visibility with no access specified. It can be used only by other classes in the same package. If we extend the class containing this default access method, the class will not have access to this method:

fairlySecretTask(){
  //allow just the classes in the package
  //Not for external use
}

Here is a last example before we move on. It contains a protected method, only visible to the package, but usable by our classes that extend it:

protected familyTask(){
  //allow just the classes in the package
  //And you can use me if you extend me too
}

Method access in a nutshell

Method access should be chosen to best enforce the principles we have already discussed. It should provide the users of your class with just the access they need, and preferably nothing more. Thereby, we achieve our encapsulation goals such as keeping the internal workings of your code safe from interference from the programs that use it, for all the reasons we have discussed.

Accessing private variables with the getter and setter methods

So if it is best practice to hide our variables away as private, how do we allow access to them without spoiling our encapsulation? What if an object of the Hospital class wanted access to the health member variable from an object of type Soldier so that it could increase it? The health variable should be private, right?

In order to be able to make as many member variables as possible private and yet allow some kind of limited access to some of them, we use getters and setters. Getters and setters are methods that just get and set variable values.

This is not some special or new Java thing we have to learn. It is just a convention for the use of what we already know. Let's take a look at getters and setters using the example of our Soldier and Hospital classes.

In this example, each of our two classes are created in their own file but the same package. First of all, here is our hypothetical Hospital class:

class Hospital{
  private void healSoldier(Soldier soldierToHeal){
    int health = soldierToHeal.getHealth();
    health = health + 10;
    soldierToHeal.setHealth(health);
  }
}

Our implementation of the Hospital class has just one method, healSoldier. It receives a reference to a Soldier object as a parameter, so this method will work on whichever Soldier object is passed in: vassily, wellington, rambo, or whoever.

It also has a health variable. It uses this variable to temporarily hold and increase the soldier's health. In the same line, it initializes the health variable to the Soldier object's current health. The Soldier object's health is private, so the public getter method is used instead.

Then health is increased by 10 and the setHealth setter method loads the new health value back to the Soldier object.

The key here is that although a Hospital object can change a Soldier object's health, it does so within the bounds of the getter and setter methods. The getter and setter methods can be written to control and check for potentially erroneous or harmful values.

Next comes our hypothetical Soldier class, with the simplest implementation possible of it's getter and setter methods:

public class Soldier{
  private int health;
  public int getHealth(){
    return health;
  }

  public void setHealth(int newHealth){
    health = newHealth;
  }
}

We have one instance variable called health and it is private. Private means it can only be changed by methods of the Soldier class. We then have a public getHealth method, which unsurprisingly returns the value held in the private health variable of the int type. As this method is public, anyone with access to the Soldier class can use it.

Next, the setHealth method is implemented. Again it is public, but this time, it takes int as a parameter and assigns whatever value is passed to the private health variable. In a more life-like example, we would write some more code here to ensure that the value passed is within the bounds we expect.

Now we will declare, create, and assign to make an object of each of our two new classes and see how our getters and setters work:

Soldier mySoldier = new Soldier();
//mySoldier.health = 100;//Doesn't work private
//we can use the public setter setHealth()
mySoldier.setHealth(100);//That's better

Hospital militaryHospital = new Hospital();

//Oh no mySoldier has been wounded
mySoldier.setHealth(10);

//Take him to the hospital
//But my health variable is private
//And Hospital won't be able to access it
//I'm doomed - tell Laura I love her

//No wait- what about my public getters and setters?
//We can use the public getters and setters from another class

militaryHospital.healSoldier(mySoldier);

//mySoldiers private variable health has been increased by 10
//I'm feeling much better thanks!

We see that we can call our public setHealth and getHealth methods directly on our object of type Soldier. Not only that, we can also call the healSoldier method of the Hospital object, passing in a reference to the Soldier object, which can use the public getters and setters to manipulate the private health variable.

We see that the private health variable is simply accessible, yet totally within the control of the designer of the Soldier class.

If you want to play around with this example, there is a working app in the code bundle in the Chapter6 folder, called Getters And Setters. I have added a few lines of code to print to the console. We deliberately covered this the way we did to keep the key parts of the code as clear as possible. We will soon build some real working examples that explore class, variable, and method access.

Note

Getters and setters are sometimes referred to by their more correct names, Accessors and Mutators. We will stick to getters and setters. Just thought you might like to know.

Yet again, our example and the explanation are probably raising more questions. That's good! Previously, I said that:

  • There are two access modifiers for a class, default and public
  • Objects of classes are a type of reference variable
  • Variables (including objects) have even more access possibilities

We need to look more closely at reference and primitive variables as well as local and instance variables. We will do so in a moment in the Variables revisited section. In that section, we will consolidate our information further to get a tighter grip on this OOP stuff. First let's remind ourselves of a bit about encapsulation.

Using encapsulation features (such as access control) is like signing a really important deal about how to use and access a class, its methods, and its variables. The contract is not just an agreement about the present but an implied guarantee for the future. We will see that as we proceed through this chapter, there are more ways that we refine and strengthen this contract.

Note

It is perfectly possible to rewrite every example in this book without thinking or caring about encapsulation. In fact, the projects in this book outside of this chapter are quite lax about encapsulation.

Use encapsulation where it is needed or, of course, if you are being paid to use it by an employer. Often encapsulation is overkill on small learning projects, such as the games in this book, except when the topic you are learning is encapsulation itself.

We are learning this Java OOP stuff under the assumption that you will one day want to write much more complex apps, whether on Android or some other platform that uses OOP.

Setting up our objects with constructors

With all of these private variables and their getters and setters, does it mean that we need a getter and a setter for every private variable? What about a class with lots of variables that need initializing at the start? Think about this:

mySoldier.name
mysoldier.type
mySoldier.weapon
mySoldier.regiment
...

We could go on like this. Some of these variables might need getters and setters, but what if we just want to set things up when the object is first created to make the object function correctly? Do we need two methods (a getter and a setter) for each?

For this, we have a special method called a constructor. Here, we create an object of type Soldier and assign it to an object called mySoldier:

Soldier mySoldier = new Soldier();

There's nothing new here, but look at the last part of that line of code:

...Soldier();

This looks suspiciously like a method.

We have called a special method called a constructor that has been supplied automatically for us by the compiler.

However (and this is getting to the point now), like a method, we can override it, which means we can do really useful things to set up our new object before it is used and any of its methods are placed on the stack:

public Soldier(){
  health = 200;
  //more setup here
}

This is a constructor. It has a lot of syntactical similarities to a method. It can only be run with the use of the new keyword. It is created for us automatically by the compiler unless we create our own like in the previous code.

Constructors have the following properties:

  • They have no return type
  • They have the same name as the class
  • They can have parameters
  • They can be overloaded

We will play with constructors in the next demo.

Variables revisited

You probably remember, back in the math game project, that we kept changing where we declared our variables. First, we declared some in onCreate, then we moved them to just below the class declaration, and then we were making them member or instance variables.

Because we didn't specify the access, they were of default access and visible to the whole class, and as everything took place in the one class, we could access them everywhere. For example, we could update our TextView type objects from onClick, but why couldn't we do that when they were declared in onCreate? Further explanation about when and how we can access different variables is probably going to be useful.

The stack and the heap

The VM inside every Android device takes care of memory allocation to our games. In addition, it stores different types of variables in different places.

Variables that we declare and initialize in methods are stored on the area of memory known as the stack. We can stick to our warehouse analogy when talking about the stack—almost. We already know how we can manipulate the stack.

Let's talk about the heap and what is stored there. All reference type objects, which include objects (of classes) and arrays, are stored in the heap. Think of the heap as a separate area of the same warehouse. The heap has lots of floor space for odd-shaped objects, racks for smaller objects, lots of long rows with smaller sized cube-shaped holes for arrays, and so on. This is where our objects are stored. The problem is that we have no direct access to the heap.

Let's look again at what exactly a reference variable is. It is a variable that we refer to and use via a reference. A reference can be loosely (but usefully) defined as an address or location. The reference (address or location) of the object is on the stack. When we use the dot operator, we are asking Dalvik to perform a task at a specific location as stored in the reference.

Note

Reference variables are just that—a reference. They are a way to access and manipulate the object (variables or methods), but they are not the actual variable. An analogy might be that primitives are right there (on the stack) but references are an address, and we say what to do at the address. In this analogy, all addresses are on the heap.

Why would we ever want a system like this? Just give me my objects on the stack already!

A quick break to throw out the trash

Remember way back in the first chapter when I said Java was easier to learn than some languages because it helps us manage the memory? Well, this whole stack and heap thing does that for us.

As we know, the VM keeps track of all our objects for us and stores them in the heap—a special area of our warehouse. Periodically, the VM will scan the stack, or the regular racks of our warehouse, and match references to objects. If it finds any objects without a matching reference, it destroys them. In Java terminology, it performs garbage collection. Think of a very discriminating refuse vehicle driving through the middle of our heap, scanning objects to match to references. No reference? You're garbage now! After all, if an object has no reference variable, we can't possibly do anything with it anyway. This system of garbage collection helps our games run more efficiently by freeing unused memory.

So variables declared in a method are local, on the stack, and only visible within the method they were declared. A member variable is on the heap and can be referenced from any place where there is a reference to it, provided the access specification allows the referencing.

Now we can take a closer look at the variable scope—what can be seen from where.

There are more twists and turns to be learned with regard to variables. In the next demo, we will explore all we have learned so far in this chapter and some new ideas too.

We will look at the following topics:

  • Static variables that are consistent (the same) across every instance of a class
  • Static methods of a class where you can use the methods of a class without an object of that class type
  • We will demonstrate the scope of class and local variables, and where they can and can't be seen by different parts of the program
  • We will look at the this keyword, which allows us to write code that refers to variables that belong to a specific instance of a class, but without keeping track of which instance we are currently using

The following is the demo.

Access, scope, this, static, and constructors demo

We have looked at the intricate way by which access to variables and their scope is controlled, and it would probably serve us well to look at an example of them in action. These will not be very practical real-world examples of variable use, but more of a demonstration to help understand access modifiers for classes, methods, and variables, alongside the different types of variables such as reference (or primitive) and local (or instance). Then we will cover the new concepts of static and final variables and the this keyword. The completed project is in the Chapter6 folder of the code download. It is called AccessScopeThisAndStatic. We will now perform the following steps to implement it:

  1. Create a new blank activity project and call it AccessScopeThisAndStatic.
  2. Create a new class by right-clicking on the existing MainActivity class in the Project Explorer and navigating to New | Class. Name the new class AlienShip.
  3. Now we declare our new class and some member variables. Note that numShips is private and static. We will soon see how this variable is the same across all instances of the class. The shieldStrength variable is private and shipName is public:
    public class AlienShip {
    private static int numShips;
    private int shieldStrength;
    public String shipName;
  4. Next is the constructor. We can see that the constructor is public, has no return type, and has the same name as the class, as per the rules. In it, we increment the private static numShips variable. Remember that this will happen each time we create a new object of the AlienShip type. The constructor also sets a value for the shieldStrength private variable using the private setShieldStrength method:
    public AlienShip(){
      numShips++;
    
      //Can call private methods from here because I am part
      //of the class
      //If didn't have "this" then this call might be less clear
      //But this "this" isn't strictly necessary
      this.setShieldStrength(100);
      //Because of "this" I am sure I am setting 
      //the correct shieldStrength
    }
  5. Here is the public static getter method that classes outside AlienShip can use to find out how many AlienShip objects are there. We will also see the unusual way in which we use static methods:
        public static int getNumShips(){
            return numShips;
    
        }
  6. The following code shows our private setShieldStrength method. We could have just set shieldStrength directly from within the class, but this code shows how we can distinguish between the shieldStrength local variable/parameter and the shieldStrength member variable using the this keyword:
    private void setShieldStrength(int shieldStrength){
        //"this" distinguishes between the 
        //member variable shieldStrength
        //And the local variable/parameter of the same name
        this.shieldStrength = shieldStrength;
        
    }
  7. This next method is the getter, so other classes can read but not alter the shield strength of each AlienShip object:
    public int getShieldStrength(){
        return this.shieldStrength;
    }
  8. Now we have a public method that can be called every time an AlienShip object is hit. It just prints to the console and then checks whether that particular object's shieldStrength is zero. If it is zero, it calls the destroyShip method, which we look at next:
    public void hitDetected(){
    
        shieldStrength -=25;
        Log.i("Incoming: ","Bam!!");
        if (shieldStrength == 0){
            destroyShip();
        }
    
    }
  9. Finally, we will look at the destroyShip method for our AlienShip class. We print a message that indicates which ship has been destroyed, based on its shipName, as well as increment the numShips static variable so that we can keep track of the number of objects of the AlienShip type we have:
    private void destroyShip(){
        numShips--;
        Log.i("Explosion: ", ""+this.shipName + " destroyed");
        }
    }
  10. Now we switch over to our MainActivity class and write some code that uses our new AlienShip class. All of the code goes in the onCreate method after the call to setContentView. First, we create two new AlienShip objects called girlShip and boyShip:
    //every time we do this the constructor runs
    AlienShip girlShip = new AlienShip();
    AlienShip boyShip = new AlienShip();
  11. Look how we get the value in numShips. We use the getNumShips method as we might expect. However, look closely at the syntax. We are using the class name and not an object. We can also access static variables with methods that are not static. We did it this way to see a static method in action:
    //Look no objects but using the static method
    Log.i("numShips: ", "" + AlienShip.getNumShips());
  12. Now we assign names to our public shipName string variables:
    //This works because shipName is public
    girlShip.shipName = "Corrine Yu";
    boyShip.shipName = "Andre LaMothe";
  13. If we attempt to assign a value directly to a private variable, it won't work. Therefore, we use the public getShieldStrength getter method to print the value of shieldStrength, which was assigned to the constructor:
    //This won't work because shieldStrength is private
    //girlship.shieldStrength = 999;
    
    //But we have a public getter
    Log.i("girlShip shieldStrngth: ", "" + girlShip.getShieldStrength());
    
    Log.i("boyShip shieldStrngth: ", "" + boyShip.getShieldStrength());
    
    //And we can't do this because it's private
    //boyship.setShieldStrength(1000000);

    Finally, we get to blow some stuff up by playing with the hitDetected method and occasionally checking the shield strength of our two objects:

    //let's shoot some ships
    girlShip.hitDetected();
    Log.i("girlShip shieldStrngth: ", "" + girlShip.getShieldStrength());
            
    Log.i("boyShip shieldStrngth: ", "" + boyShip.getShieldStrength());
            
    boyShip.hitDetected();
    boyShip.hitDetected();
    boyShip.hitDetected();
            
    Log.i("girlShip shieldStrngth: ", "" + girlShip.getShieldStrength());
            
    Log.i("boyShip shieldStrngth: ", "" + boyShip.getShieldStrength());
            
    boyShip.hitDetected();//ahhh
            
    Log.i("girlShip shieldStrngth: ", "" + girlShip.getShieldStrength());
    
    Log.i("boyShip shieldStrngth: ", "" + boyShip.getShieldStrength());
  14. When we think we have destroyed a ship, we again use our static getNumShips method to check whether our static variable numShips was changed by the destroyShip method:
      
    Log.i("numShips: ", "" + AlienShip.getNumShips());
  15. Run the demo and look at the console output.

Here is the output of the preceding blocks of code:

numShips:﹕ 2
girlShip shieldStrngth:﹕ 100
boyShip shieldStrngth:﹕ 100
Incomiming:﹕ Bam!!
girlShip shieldStrngth:﹕ 75
boyShip shieldStrngth:﹕ 100
Incomiming:﹕ Bam!!
Incomiming:﹕ Bam!!
Incomiming:﹕ Bam!!
girlShip shieldStrngth:﹕ 75
boyShip shieldStrngth:﹕ 25
Incomiming:﹕ Bam!!
Explosion:﹕ Andre LaMothe destroyed
girlShip shieldStrngth:﹕ 75
boyShip shieldStrngth:﹕ 0
numShips:﹕ 1
boyShip shieldStrngth:﹕ 0
numShips:﹕ 1

In the previous example, we saw that we can distinguish between local and member variables of the same name using the this keyword. We can also use the this keyword to write code that refers to the current object being acted upon.

We saw that a static variable, in this case numShips, is consistent across all instances. Moreover, by incrementing it in the constructor and decrementing it in our destroyShip method, we can keep track of the number of AlienShip objects we created.

We also saw that we can use static methods by writing the class name with the dot operator instead of an object.

Finally, we demonstrated how we could hide and expose certain methods and variables using an access specifier.

Let's take a look at a quick review of the stack and the heap before we move on to something new.

A quick summary on stack and heap

Let's look at what we learned about the stack and the heap:

  • You don't delete objects but the VM sends the garbage collector when it thinks it is appropriate. This is usually done when there is no active reference to the object.
  • Local variables and methods are on the stack, and local variables are local to the specific method within which they were declared.
  • Instance or class variables are on the heap (with their objects) but the reference to the object (address) is a local variable on the stack.
  • We control what goes inside the stack. We can use the objects on the heap but only by referencing them.
  • The heap is maintained by the garbage collector.
  • An object is garbage collected when there is no longer a valid reference to it. Therefore, when a reference variable, local or instance, is removed from the stack, then its related object becomes viable for garbage collection, and when the virtual machine decides the time is right (usually very promptly), it will free the RAM memory to avoid running out.
  • If we try to reference an object that doesn't exist, we will get a null pointer exception and the game will crash.

Inheritance

We have seen how we can use other people's hard work by instantiating/creating objects from the classes of an API such as that of Android, but this whole OOP thing goes even further than that.

What if there is a class that has loads of useful functionality in it but not quite what we want? We can inherit from the class and then further refine or add to how it works and what it does.

You might be surprised to hear that we have done this already. In fact, we have done this with every single game and demo we looked at. When we use the extends keyword, we are inheriting, for example, in this line of code:

public class MainActivity extends Activity ...

Here, we are inheriting the Activity class along with all its functionality, or more specifically, all of the functionality that the class designers want us to have access to. Here are some of the things we can do to classes we have extended.

We can override a method and still rely in part on the overridden method in the class we inherit from. For example, we overrode the onCreate method every time we extended the Activity class, but we also called the default implementation provided by the class designers when we did this:

super.onCreate(... 

In the next chapter, we will also be overriding some more methods of the Activity class. Specifically, we'll override the methods that handle the lifecycle.

If we or the designer of a class wants to force us to inherit before we use their class, they can declare a class as abstract. Then we cannot make an object from it. Therefore, we must extend it first and make an object from the subclass. We will do this in our inheritance example and discuss it further when we look at polymorphism.

We can also declare a method abstract, and that method must be overridden in any class that extends the class with the abstract method. We will do this as well in our inheritance example.

In our game projects, we will not be designing any classes that we will be extending. We have no need of that in the context of learning about building simple games. However, we will be extending classes designed by others in every future game.

We discuss inheritance mainly so that we understand what is going on around us and as the first step towards being able to eventually design useful classes that we or others can extend. With this in mind, let's make some simple classes and see how we can extend them, just to play around with the syntax as a first step, and also to be able to say we have done it. When we look at the last major topic of this chapter, polymorphism, we will also dig a little deeper into inheritance.

An example of inheritance

We have looked at the way we can create hierarchies of classes to model the system that fits our game or software project, so let's try out some simple code that uses inheritance. The completed project is in the Chapter6 folder of the code download. It is called InheritanceExample. We will now perform the following steps:

  1. Create three new classes in the usual way. Call one AlienShip, another Fighter, and the last one Bomber.
  2. Here is the code for the AlienShip class. It is very similar to our previous AlienShip class demo. The differences are that the constructor now takes an int parameter, which it uses to set the shield strength. The constructor also outputs a message to the console so that we can see when it is being used. The AlienShip class also has a new method, fireWeapon, that is declared abstract. This guarantees that any class that subclasses AlienShip must implement their own version of fireWeapon. Notice that the class has the abstract keyword as part of its declaration. We have to do this because one of its methods also uses the keyword abstract. We will explain the abstract method when discussing this demo and the abstract class when we talk about polymorphism:
    public abstract class AlienShip {
        private static int numShips;
        private int shieldStrength;
        public String shipName;
    
        public AlienShip(int shieldStrength){
            Log.i("Location: ", "AlienShip constructor");
            numShips++;
            setShieldStrength(shieldStrength);
        }
    
        public abstract void fireWeapon();//Ahh my body
    
        public static int getNumShips(){
            return numShips;
        }
    
        private void setShieldStrength(int shieldStrength){
            this.shieldStrength = shieldStrength;
        }
    
    
        public int getShieldStrength(){
            return this.shieldStrength;
        }
    
        public void hitDetected(){
            shieldStrength -=25;
            Log.i("Incomiming: ", "Bam!!");
            if (shieldStrength == 0){
                destroyShip();
            }
    
        }
    
        private void destroyShip(){
            numShips--;
            Log.i("Explosion: ", "" + this.shipName + " destroyed");
        }
    
    }
  3. Now we will implement the Bomber class. Notice the call to super(100). This calls the constructor of the superclass with the value for shieldStrength. We could do further specific Bomber initialization in this constructor, but for now, we just print the location so that we can see when the Bomber constructor is being executed. We also implement a Bomber class-specific version of the abstract fireWeapon method because we must do so:
    public class Bomber extends AlienShip {
    
        public Bomber(){
            super(100);
            //Weak shields for a bomber
            Log.i("Location: ", "Bomber constructor");
        }
    
        public void fireWeapon(){
            Log.i("Firing weapon: ", "bombs away");
        }
    }
  4. Now we will implement the Fighter class. Notice the call to super(400). This calls the constructor of the superclass with the value of shieldStrength. We could do further Fighter class-specific initialization in this constructor, but for now, we just print the location so that we can see when the Fighter constructor is being executed. We also implement a Fighter specific version of the abstract fireWeapon method because we must do so:
    public class Fighter extends AlienShip{
    
        public Fighter(){
            super(400);
            //Strong shields for a fighter
            Log.i("Location: ", "Fighter constructor");
        }
    
        public void fireWeapon(){
            Log.i("Firing weapon: ", "lasers firing");
        }
    
    }
  5. Here is our code in the onCreate method of MainActivity. As usual, we enter this code after the call to setContentView. This is the code that uses our three new classes. It looks quite ordinary, but there's nothing new; it is the output that is interesting:
    Fighter aFighter = new Fighter();
    Bomber aBomber = new Bomber();
    
    //Can't do this AlienShip is abstract -
    //Literally speaking as well as in code
    //AlienShip alienShip = new AlienShip(500);
    
    //But our objects of the subclasses can still do
    //everything the AlienShip is meant to do
    aBomber.shipName = "Newell Bomber";
    aFighter.shipName = "Meier Fighter";
    
    //And because of the overridden constructor
    //That still calls the super constructor
    //They have unique properties
    Log.i("aFighter Shield:", ""+ aFighter.getShieldStrength());
    Log.i("aBomber Shield:", ""+ aBomber.getShieldStrength());
    
        //As well as certain things in certain ways
        //That are unique to the subclass
        aBomber.fireWeapon();
        aFighter.fireWeapon();
    
        //Take down those alien ships
        //Focus on the bomber it has a weaker shield
        aBomber.hitDetected();
        aBomber.hitDetected();
        aBomber.hitDetected();
        aBomber.hitDetected();

Here is the output of the preceding snippets of code:

Location:﹕ AlienShip constructor
Location:﹕ Fighter constructor
Location:﹕ AlienShip constructor
Location:﹕ Bomber constructor
aFighter Shield:﹕ 400
aBomber Shield:﹕ 100
Firing weapon:﹕ bombs away
Firing weapon:﹕ lasers firing
Incomiming:﹕ Bam!!
Incomiming:﹕ Bam!!
Incomiming:﹕ Bam!!
Incomiming:﹕ Bam!!
Explosion:﹕ Newell Bomber destroyed

We can see how the constructor of the subclass can call the constructor of the superclass. We can also clearly see that the individual implementations of the fireWeapon method work exactly as expected.

As if OOP where not useful enough already! We can now model real-world objects and design them to interact with each other. We have also seen how we can make OOP even more useful by subclassing/extending/inheriting from other classes. The terminology we might like to learn here is that the class that is extended from is the superclass and the class that inherits from the superclass is the subclass. We can also call them parent and child classes.

Tip

As usual, we might find ourselves asking this question about inheritance: Why? We can write common code once, in the parent class, and we can update that common code. All the classes that inherit from it are also updated. Furthermore, a subclass only inherits public instance variables and methods. When designed properly, this further enhances the goals of encapsulation.

Polymorphism

Polymorphism roughly means different forms. But what does it mean to us?

In the simplest words possible, any subclass can be used as a part of the code that uses the superclass.

For example, if we have an array of animals, we could put any object that is of a type that is a subclass of Animal in the Animal array, perhaps cats and dogs.

This means that we can write code that is simpler and easier to understand and modify:

//This code assumes we have an Animal class
//And we have a Cat and Dog class that extends Animal
Animal myAnimal = new Animal();
Dog myDog = new Dog();
Cat myCat = new Cat();
Animal [] myAnimals = new Animal[10];
myAnimals[0] = myAnimal;//As expected
myAnimals[1] = myDog;//This is OK too
myAnimals[2] = myCat;//And this is fine as well

We can also write code for the superclass and rely on the fact that no matter how many times it is subclassed, within certain parameters, the code will still work. Let's continue our previous example:

//6 months later we need elephants
//with its own unique aspects
//As long as it extends Animal we can still do this
Elephant myElephant = new Elephant();
myAnimals[3] = myElephant;//And this is fine as well

You can also write methods with polymorphic return types and arguments:

Animal feedAnimal(Animal animalToFeed){
  //Feed any animal here
  return animalToFeed;
}

So you can even write code today and make another subclass in a week, month, or year, and the very same methods and data structures will still work.

Further, we can enforce on our subclasses a set of rules as to what they can and cannot do, and also how they should do it. Thus, good design in one stage can influence our subclasses at other stages.

If you do suddenly find yourself with a flappy-bird-sized phenomenon, and you have a lot of OOP in your code, right from the start, it will be much easier to bring in hired help to move the project forward and still maintain control of the project.

What if you have an idea for a game with lots of features but you want to get a simple version of the game out as soon as possible? Smart, object-oriented design would certainly be the solution. It could enable you to write the working bare bones of a game and then gradually extend it.

Moving on, let's look at another OOP concept: abstract classes. We can now get to the bottom of what was going on with that AlienShip code:

public abstract class AlienShip{...

Abstract classes

An abstract class is a class that cannot be instantiated, or cannot be made into an object. We mentioned that AlienShip was abstract in the previous example. So is it a blueprint that will never be used then? But that's like paying an architect to design your home and then never building it! I kind of got the idea of an abstract method but this is just silly!

It might seem like this at first. We make a class abstract by declaring it with the abstract keyword, like this:

abstract class someClass{
  //All methods and variables here as usual
  //Just don't try and make an object out of me!
}

But why?

Sometimes, we want a class that can be used as a polymorphic type but we need to ensure that it can never be used as an object. For example, Animal doesn't really make sense on its own.

We don't talk about animals; we talk about types of animals. We don't say, "Ooh, look at that lovely, fluffy, white animal", or "Yesterday, we went to the pet shop and got an animal and an animal bed." It's just too abstract.

So an abstract class is like a template to be used by any class that extends it (inherits from it).

We might want a Worker class and extend to make classes such as Miner, Steelworker, OfficeWorker, and of course Programmer. But what exactly does a plain Worker class do? Why would we ever want to instantiate one?

The answer is that we wouldn't want to instantiate it, but we might want to use it as a polymorphic type so that we can pass multiple worker subclasses between methods and have data structures that can hold all types of workers.

We call this type of class an abstract class, and when a class has even one abstract method, like AlienShip did, it must be declared abstract itself. As we saw, all abstract methods must be overridden by any class that extends the abstract class. This means that the abstract class can provide some of the common functionality that would be available in all its subclasses. For example, the Worker class might have the height, weight, and age member variables.

It might have the getPayCheck method, which is the same in all the subclasses, and the doWork method, which is abstract and must be overridden because all the different types of worker do work very differently.

This leads us neatly on to another area of polymorphism that deserves an honorable mention because we have been using it in every game so far.

Interfaces

An interface is like a class. Phew! Nothing complicated here then. However, it's like a class that is always abstract and with only abstract methods.

We can think of an interface as an entirely abstract class with all its methods abstract too. Okay, so you can just about wrap your head round an abstract class because it can at least pass on some functionality in its methods that are not abstract and serve as a polymorphic type.

But seriously, this interface seems a bit pointless. Bear with me.

To define an interface, we type the following code:

public interface myInterface{
  void someAbstractMethod();//omg I've got no body
  int anotherAbstractMethod();//Ahh! Me too

//Interface methods are always abstract and public implicitly 
//but we could make it explicit if we prefer

  public abstract explicitlyAbstractAndPublicMethod();//still no body though


}

The methods of an interface have no body because they are abstract, but they can still have return types and parameters, or not.

To use an interface, we use the implements keyword after the class declaration. Yes, we already did this for onClickListener a few times:

public class someClass implements someInterface{

//class stuff here

//better implement the methods of the interface or the red error lines will not go away
  public void someAbstractMethod(){
    //code here if you like but just an empty implementation will do
  }

  public int anotherAbstractMethod(){
    //code here if you like but just an empty implementation will do

    //Must have a return type though as that is part of the contract
    return 1;}
}

This enables us to use polymorphism with multiple different objects that are from completely unrelated inheritance hierarchies. As long as it implements an interface, the whole thing can be passed along as if it is that thing, which it is. We can even have a class implement multiple different interfaces at the same time. Just add a comma between each interface and list them after the implements keyword. Just be sure to implement all the necessary methods.

Let's go back to the onClickListener interface. Any thing might like to know when it is being clicked on; a Button, a TextView, and so on. We don't want different onClick methods for every type.

Tip

When using Android, for games or for more regular GUI-based apps (a bit like ours so far), 9 times out of 10, you will be implementing interfaces rather than writing your own. However, knowing what is happening is quite important, not so much from a point of view of technical awareness, as we have just seen that the interface specifies a contract and the compiler enforces it, but more as a matter of sanity in knowing what is actually happening when you use the implements keyword and write a method (or methods) with a name that you didn't choose.

More about OOP and classes

It is possible to write a whole book on OOP, and many authors have already done so, but the best way to learn OOP is probably to practice it; practice it before we have learned all of the theory. Anyway, before we get on with some more practical examples, here is one more slightly theoretical OOP example that will leave us scratching our heads later if not mentioned.

Inner classes

When we looked at our basic classes demo app, we declared and implemented the class in a separate file to our MainActivity class. That file had the same name as the class.

We can also declare and implement a class within a class. The only question remaining, of course, is why would we do this? When we implement an inner class, the inner class can access the member variables of the enclosing class and the enclosing class can access the members of the inner class. We will see this in action in the next two chapters.

If you are not modeling deep or real-world systems, then inner classes are often the way to go. In fact, all the classes we will write ourselves in the rest of this book will be extended inner classes. This means that we will extend a type to make our own class within our Activity class. This makes our code nice and simple.

Self-test questions

Q1) Find out what is wrong with this class declaration:

  private class someClass{
    //class implementation goes here
  }

Q2) What is encapsulation?

Q3) I don't get it all, and actually, I have more questions now than I had at the start of the chapter. What should I do?

Summary

In this chapter, we covered more theory than in any other chapter. If you haven't memorized everything, then you have succeeded completely. If you just understand that OOP is about writing reusable, extendable, and efficient code through encapsulation, inheritance, and polymorphism, then you have the potential to be a Java master. Simply put, OOP enables us to use other people's hard work even when those people were not aware of exactly what we would be doing at the time they did the work. All you have to do is keep practicing, so let's make a retro game in the next chapter.