Accessing private variables with getters and setters

Now we need to consider, if it is best practice to hide our variables away as private, how do we allow access to them, where necessary, without spoiling our encapsulation? What if an object of class Hospital wanted access to the health member variable from an object of type Soldier, so it could increase it? The health variable should be private because we don't want just any piece of code changing it.

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

Getters and setters are not some special new Java thing we have to learn. It is just a convention for using what we already know. Let's have a look at getters and setters using our Soldier and Hospital class example.

In this example, each of our two classes is created in their own file but the same package. First, here is our 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 local health variable which it uses 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 getHealth, is used instead.

Then health is increased by 10 and the setHealth setter method loads up the new revived health value, back to the Soldier object. Perhaps the value 10 could be changed to a member variable of Hospital. Then, in the RTS, as the hospital is upgraded, the amount the health is restored by could increase.

The key here is that although a Hospital object can change a Soldier object's health; it only 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 look at our hypothetical Soldier class with the simplest implementation possible of its getter and setter methods.

public class Soldier{
   
   private int health;

   public int getHealth(){
          return health;
   }

   public void setHealth(int newHealth){

      // Check for stupid values of newHealth
      health = newHealth;
   }
}

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

Next, the setHealth method is implemented. Again, it is public but this time it takes an int as a parameter and assigns whatever is passed in, to the private health variable.

Tip

In a more life-like example, we would write some more code here to make sure the value passed in to setHealth is within the bounds we expect.

Now we 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;// Does not 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);

// mySoldier's 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 call the healSoldier method of the Hospital object, passing in a reference to the Soldier object, which too 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.

Note

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

Yet again our example and the explanation is probably raising more questions than it answers. That's good.

By using encapsulation features (like access control) it is kind of like agreeing on an important deal/contract about how to use and access a class, its methods, and variables. The contract is not just an agreement about now, 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.

Tip

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, like some of the examples in this book. Except, of course, 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 games, whether on Android or some other platform which uses OOP. In addition, we will be using classes from the Android API that use it extensively and it will help us understand what is happening then as well. Typically, throughout this book, we will use encapsulation when implementing full game projects and often overlook it when showing small code samples to demonstrate a single idea or topic.