- Learning Java by Building Android Games
- John Horton
- 1187字
- 2021-07-23 19:02:33
Encapsulation and static methods mini-app
We have looked at the intricate way that 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 so much be practical real-world examples of variable use, more a demonstration to help understand access modifiers for classes, methods, and variables alongside the different types of a variable like a reference or primitive and local or instance, along with the new concepts of static and final variables and the this keyword. The completed code is in the chapter 8
folder of the download bundle. It is called Access Scope This And Static
.
Create a new Empty Activity project and call it Access Scope This And Static
. Leave the Activity name at the default, MainActivity
.
Note
Creating a new project will hopefully be straightforward by now. Refer back to Chapter 1 if you would like a refresher.
Create a new class by right-clicking on the existing MainActivity
class in the project window and clicking New | Class. Name the new class AlienShip
.
Tip
This is just another way of creating a new class. In the previous mini-app, we used File | New | Java Class.
Now we declare our new class and some member variables. Note that numShips
is private
and static
. We will see how this variable is the same across all instances of the class, soon. The shieldStrength
variable is private
, shipName
is public
.
public class AlienShip { private static int numShips; private int shieldStrength; public String shipName;
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 (add one too) the private static numShips
variable. We will see that this will happen each time we create a new object of type AlienShip
. Also, the constructor sets a value for the private variable shieldStrength
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 Because of "this" I am sure I am setting the correct shieldStrength */ this.setShieldStrength(100); }
Here is the public static
getter method so classes outside of AlienShip
can find out how many AlienShip
objects there are. We will also see the way in which we use static methods.
public static int getNumShips(){ return numShips; }
And this is our private setShieldStrength
method. We could have just set shieldStrength
directly from within the class but the code below shows how we distinguish between the shieldStrength
local variable/parameter and the shieldStrength
member variable by 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; }
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; }
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 detects if that object's shieldStrength
is zero. If it is, it calls the destroyShip
method that we look at next.
public void hitDetected(){ shieldStrength -=25; Log.i("Incoming: ","Bam!!"); if (shieldStrength == 0){ destroyShip(); } }
And lastly, for our AlienShip
class, we will code the destroyShip
method. We print a message that indicates which ship has been destroyed based on its shipName
as well as decrement the numShips
static variable so we can keep track of how many objects of type AlienShip
we have.
private void destroyShip(){ numShips--; Log.i("Explosion: ", ""+ this.shipName + " destroyed"); } } // End of the class
Now we switch over to our MainActivity
class and write some code that uses our new AlienShip
class. All the code goes in the onCreate
method after the call to super.onCreate
. 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();
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());
Now we assign names to our public shipName
String variables.
// This works because shipName is public girlShip.shipName = "Corrine Yu"; boyShip.shipName = "Andre LaMothe";
If we attempt to assign a value directly to a private
variable- It won't work. Then we use the public getter method getShieldStrength
to print out the shieldStrength
which was assigned in 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 shieldStrength
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());
When we think we have destroyed a ship, we again use our static getNumShips
method to see if our static variable numShips
was changed by the destroyShip
method.
Log.i("numShips: ", "" + AlienShip.getNumShips());
Run the mini-app and look at the logcat output.
numShips: 2 girlShip shieldStrngth: 100 boyShip shieldStrngth: 100 Incoming: Bam!! girlShip shieldStrngth:﹕ 75 boyShip shieldStrngth:﹕ 100 Incoming: Bam!! Incoming: Bam!! Incoming: Bam!! girlShip shieldStrngth:﹕ 75 boyShip shieldStrngth:﹕ 25 Incoming: 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 by using the this
keyword. We can also use the this
keyword to write code which refers to whatever the current object being acted upon is.
We saw that a static variable, in this case, numShips
, is consistent across all instances. Furthermore, by incrementing it in the constructor and decrementing it in our destroyShip
method, we can keep track of the number of AlienShip
objects we currently have.
We also saw that we can use static methods by using the class name with the dot operator instead of an actual object. Yes, I know it is kind of like living in the blueprint of a house- but it's quite useful.
Finally, we demonstrated how we could hide and expose certain methods and variables using an access specifier.