OOP 7. Private data - TimeOfDay
- Make data private for reliability.
- Write getters and setters to work with private data.
- Instance methods can use the instance variables (fields).
private
keyword.- Methods defined without the
static
keyword are instance methods. - Naming conventions for fields.
- Only define getters for values that people need to know about.
- Setters are often not defined so that immutable objects can be created.
Bulletproofing with private, getters, and setters
To make reliable software, it's essential to protect the data in an
object - insure that it is initialized, and prevent illegal or
inconsistent values. The key to this is to declare the
fields private
.
Private data also reduces the potential complexity by preventing unintended coupling -- references to fields inside your class.
Bad Practice - Excessive Coupling
Risk. Public fields put the integrity of objects at great risk. Nothing prevents the following bad statements from being executed if the fields are public.
TimeOfDay3 goodTime = new TimeOfDay2a(11, 11); // Legal values.
goodTime.minute = 99; // AVOIDS CONSTRUCTOR VALIDITY CHECK
But who would do that? Of course no programmer would intentionally set the value to something illegal like this, but there could be several reasons to mistakenly set it. Here are two examples.
goodTime.minute = userValue; // FAILED TO CHECK INPUT VALUE FIRST.
goodTime.minute++; // WHAT HAPPENS AFTER 59?
Solution - Private fields, public methods to get/set them
Private data. The key to making classes safer and easier to change is to
declare fields private
. Constructors and methods in
the same class can use the field variables, but no one outside the class can see
them. However, if they're private, something must be done to allow getting and setting the values.
This is done with so called getter and setter methods.
Getters and Setters
Naming. If it's appropriate for the user of a class to get or set field values, the convention is to write public getting and setting methods. The Java convention is to begin such method name with "get" or "set" followed by the attribute that should be obtained or set.
Final parameters. A good case can be made for declaring setter parameters final
.
See Code Advice #12: Use final on constructor and setter parameters.
Getters and setters can be evil. Don't simply write getter and setter methods for every private instance variable.
- They can make your code inflexibly dependent on a particular implementation.
- There is often no need for others to know about every internal field.
- Don't write both a getter and setter if both are not appropriate.
- Getter and setter methods do not even need to correspond to specific fields -- they provide a user interface to your class, but internally you are free to make changes. We'll make such a change to the TimeOfDay class shortly.
It seems awkward to write these, and some languages and IDEs have made this either automatic or easier to do. There is controversy about how easy it should be go create getters and setters, because they shouldn't exist for all instance variables. For Java just accept that you will have to type these definitions. It's really only a couple of minutes of work.
Alternative to getters/setters. One philosophy that pervades OOP is that you should be asking a class to perform a service for you, not just give you data. If your class is a simple "value" class -- a class which just represents a simple value, it may be appropriate to have getters and setters. As a class becomes more complex, it may be more appropriate to supply methods which do the necessary computation rather than letting the user get the values and do the computation themselves.
Instance, not static, methods
Getter and setter methods are instance, not static, methods. Instance methods can reference instance variables, but static methods can not. Instance variables can only be referenced if there is an object (instance) that holds them, of course.
A note about field names - a _convention
When you read long methods it isn't always obvious whether a variable is a parameter, local variable, field (instance variable), or class (static) variable. Because instance variables have such a distinct meaning, many programmers find it useful to prefix field names with something special.
Variations on the instance variable name "hour"
_hour | Common, although it violates some Java style conventions. I use it frequently. | |
myHour | Less common, but you will see it sometimes. | |
hour | Instance variable looks like other variables. Common, but unhelpful. | |
m_hour | A C++ convention for "member" variables. Common in Java too. | |
mHour | Another common convention originating in C++. | |
fHour | The "f" denotes "field" variable. |
Controversial. Like brace conventions, field prefixes are controversial. I highly recommend naming instance variables differently than local variables, but when you collaborate with others on a project there will often be a naming convention which you must use, or your instructor may insist on one style. Therefore be flexible on this topic.
Other kinds of variables. Some programmers use naming conventions for parameters (eg, start names with "p") or class (static) variables (eg, start names with "c"). Because parameters are essentially local variables, there isn't as much interest in distinguishing them, but class variables are seriously different, and it's good practice to name them differently. But because they're relatively rare, there hasn't been as much controversy or consensus for a naming convention.
TimeOfDay3
Here's the TimeOfDay class, rewritten with private instance variables and getters and setters.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
// File : oop/timeofday/TimeOfDay3.java // Purpose: A 24 hour time-of-day class to demo intro OOP concepts. // Author : Fred Swartz - 2006-09-18 - Placed in the public domain. // Issues : Makes variables private, adds getters and setters. // Can be improved by calling setters from constructor. public class TimeOfDay3 { //========================================= instance variables private int _hour; private int _minute; //================================================ constructor public TimeOfDay3(int h, int m) { //... Check values for validity. if (h < 0 || h > 23 || m < 0 || m > 59) { throw new IllegalArgumentException( "TimeOfDay: Bad constructor value: " + h + ":" + m); } _hour = h; _minute = m; } //================================================== getHour public int getHour() { return _hour; } //================================================== setHour public void setHour(int h) { if (h < 0 || h > 23) { throw new IllegalArgumentException( "TimeOfDay setHour: Bad hour value: " + h); } _hour = h; } //================================================ getMinute public int getMinute() { return _minute; } //================================================ setMinute public void setMinute(int m) { if (m < 0 || m > 59) { throw new IllegalArgumentException( "TimeOfDay setMinute: Bad minute value: " + m); } _minute = m; } } |
See one of the problems below for a way to improve this class.
Test program
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// File : oop/timeofday/TimeTest3.java // Purpose: Test the TimeOfDay3 class.. // Author : Fred Swartz - 2006-09-18 - Placed in public domain. import javax.swing.*; public class TimeTest3 { public static void main(String[] args) { TimeOfDay3 then = new TimeOfDay3(8, 35); TimeOfDay3 now = new TimeOfDay3(14, 5); //... Print the hours and minutes of the times. JOptionPane.showMessageDialog(null, "From " + then.getHour() + ":" + then.getMinute() + " to " + now.getHour() + ":" + now.getMinute()); // THE FOLLOWING WOULD BE ILLEGAL // now._Hour = 99; // Can't reference private field. } } |
Terminology note
Java uses the terms getter / setter, but you may also see the C++ terms accessor / mutator.
Programming exercises
- Improvement. This class definition above is OK, but it could be improved by calling the setter methods from the constructor so that code isn't repeated. This code violates the DRY (Don't Repeat Yourself) principle. Rewrite the constructor with this improvement.
- Make the Student class
(see 3. Student - Constructor)
robust with private instance variables,
getters and setters, and checks for legal values
(eg, name strings must not be
null
or the empty string and the id must be in a certain range).