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.