Object-oriented programming makes use of a technique called encapsulation. If we revise last week's Cat class:
public class Cat { private String name; private int age, weight; public Cat (String nameIn, int ageIn, int weightIn) { this.name = nameIn; this.age = ageIn; this.weight = weightIn; } public void walk() { this.weight--; } public void display() { System.out.println("Name: " + this.name + " Age: " + this.age + " Weight: " + this.weight); } }you can see the use of the keywords private and public throughout the code. What do these mean?
This is common practice and is known as encapsulation. Encapsulation means to keep the inner workings of the class hidden from the outside world, and control access to those inner workings by means of the methods, which act as "gateways" between the outside world and the interior of the class.
Why is encapsulation performed? Consider this new version of the Cat class:
public class Cat { private String name; private int age, weight; public Cat (String nameIn, int ageIn, int weightIn) { this.name = nameIn; this.age = ageIn; this.weight = weightIn; } public void walk() { if(this.weight <= 5) { System.out.println("Can't walk any further... poor cat will be starved!"); } else { this.weight--; } } public void display() { System.out.println("Name: " + this.name + " Age: " + this.age + " Weight: " + this.weight); } }This new version includes an if statement inside the walk() method, which prevents the cat from walking if the weight is 5 or less. Thus we are controlling how the cat's weight can be altered using the walk() method. So if we tried the following in the main():
public class EncapsulationApp { public static void main (String[] args) { Cat tigger = new Cat ("Tigger", 5 , 7); tigger.walk(); tigger.walk(); tigger.walk(); } }The first two calls to walk() would succeed, as the weight would be reduced from 7 to 6 and then from 6 to 5. However the third call to walk() would fail, as the weight would now be 5 and cannot be reduced any further.
Note how we have used encapsulation to prevent unrealistic things happening. The following code will not compile but only because weight is private:
public class NoEncapsulationApp { public static void main (String[] args) { Cat tigger = new Cat ("Tigger", 5 , 7); tigger.weight = -1; } }If the weight was not private, you would legitimately be able to set it to -1 from the main() as in the above example. This illustrates the whole concept of encapsulation: to keep the inner workings of a class private and control access from the outside world, to prevent the outside world doing unrealistic things.
It is fairly common in object-oriented programming that we wish to access a single attribute, such as, for example, the name of the cat. We wish to keep this attribute private, so that the outside world cannot arbitarily change it, but we still wish the outside world to access it. How can we do this? We can use an accessor method, also referred to as a "getter". Here is a version of the Cat class with accessor methods for the three attributes:
public class Cat { private String name; private int age, weight; public Cat (String nameIn, int ageIn, int weightIn) { this.name = nameIn; this.age = ageIn; this.weight = weightIn; } public void walk() { if(this.weight <= 5) { System.out.println("Can't walk any further... poor cat will be starved!"); } else { this.weight--; } } public void display() { System.out.println("Name: " + this.name + " Age: " + this.age + " Weight: " + this.weight); } public String getName() { return this.name; } public int getAge() { return this.age; } public int getWeight() { return this.weight; } }
Note how these three methods, getName(), getAge() and getWeight(), are returning the corresponding attribute. Note also how we specify the return type when writing the method:
public String getName()When writing methods, the return type always immediately precedes the method name. So we are saying that the getName() method returns a String. Likewise, getAge() and getWeight() both return an int.
The example below shows how we can use the accessor methods in the main() to get individual pieces of information about the cat object:
public class AccessorsApp { public static void main (String[] args) { Cat tigger = new Cat ("Tigger", 5 , 7); Cat tom = new Cat ("Tom", 10, 10); System.out.println (tigger.getName()); System.out.println (tigger.getAge()); System.out.println (tom.getWeight()); } }Note how we use commands such as:
System.out.println (tigger.getName());What this line is doing is printing the return value of the getName() method, which, as we can see above, is the name attribute of the Cat object.
This example illustrates how we can use getter methods to provide "read" access to an attribute but to prevent "write" access. We can obtain the value of the attribute by using the getter method, but we cannot change its attribute. This is a further example of encapsulation: we allow the outside world to access the attributes but we do not allow the outside world to arbitrarily change them.
As well as accessor methods, we can also add mutator methods to update attributes. These are also called setter methods because we use them to set data. However, unlike accessors, they typically have some sort of controls to prevent the data being set to unrealistic values. For example, a setWeight() method for a Cat would probably prevent the weight being set to less than 0. An example of such a method is shown below.
public class Cat { private int weight; // ...other code omitted... public void setWeight (int newWeight) { if(newWeight > 0) { this.weight = newWeight; } else { System.out.println("Cannot set weight to 0 or less."); } } }
You have already seen the concept of passing parameters to a constructor. In Introduction to Programming and Problem Solving, you also passed parameters to functions. Hopefully it should not then be too much of a surprise that you can also pass parameters to regular Java methods. If we take a look at another version of our Cat class:
public class Cat { private String name; private int age, weight; public Cat (String nameIn, int ageIn, int weightIn) { this.name = nameIn; this.age = ageIn; this.weight = weightIn; } public void walk (int distance) { this.weight -= distance; } public void display() { System.out.println("Name: " + this.name + " Age: " + this.age + " Weight: " + this.weight); } }Note how the walk method now takes one parameter, representing the distance walked. We also reduce the weight by the distance. (Note that this.weight -= distance; is a shorter way of writing this.weight = this.weight - distance;; the -= operator reduces a variable by a given value). This could be called in a main() as follows:
public class CatAppWithParameters { public static void main (String[] args) { Cat tigger = new Cat ("Tigger", 10, 10); tigger.walk (5); tigger.display(); tigger.walk (3); tigger.display(); } }Note how we are passing the distance to the walk method. Note the difference between arguments and parameters. The value passed into a method is called an argument, whereas the parameter is the variable in the method representing that value. So, here, 5 and 3 are the arguments whereas distance is the parameter.
A method can have more than one parameter. This version of the Cat class has a modified walk() method which makes the cat lose more weight if they walk faster (the weight is reduced by the distance multiplied by the speed):
public class Cat { private String name; private int age, weight; public Cat (String nameIn, int ageIn, int weightIn) { this.name = nameIn; this.age = ageIn; this.weight = weightIn; } public void walk (int distance, int speed) { this.weight -= distance*speed; } public void display() { System.out.println("Name: " + this.name + " Age: " + this.age + " Weight: " + this.weight); } }This could then be called in the main() as follows:
public class CatAppWithMultipleParameters { public static void main (String[] args) { Cat tigger = new Cat ("Tigger", 10, 15); tigger.walk (5, 1); tigger.display(); tigger.walk (3, 2); tigger.display(); } }The cat initially walks a distance of 5 units with a speed of 1, and then walks a distance of 3 units with a speed of 2. So for the first walk() the cat will lose 5 units of weight and for the second walk the cat will lose 6 units of weight.