Next page | Contents page |

Getters and setters

Encapsulation

Objects comprise data (Java: fields) and behaviour (Java: methods).

public class Person
{
   private String name;

   public String getName () { return name; }
   public void setName (String aName) { name = aName; }
}

Good practice says make the fields private.

So we add accessors (getter and setter methods) to access them.

Q: Why is this better than simply making the data public?

A: In most cases it isn't!

What does the getter method return? A copy of the field. That's fine if the field is of a primitive type but if it is a class type the getter method is giving its caller a reference to the object in the field. The caller then has carte blanche to modify it (perhaps accidentally or maliciously). That really must not be allowed - the object whose field it is should be the one to modify it when it needs modifying. Put another way, the owning object should have control of its own state. We should only be able to change the state of an object by calling methods that it makes public as the definition of its behaviour. If it is a deliberate part of the design that it should provide a method called changeFieldX () (or which has that effect) that is fine, but allowing others to change a field should always be made explicit like that.

IDEs (Eclipse, NetBeans) have handy menu options to generate getters and setters for all fields automatically. That doesn't mean you should use them. Think carefully about whether each field needs a getter or, separately, a setter. And does the getter simply return a reference to the object in the field?

Consider the on-line ordering system we looked at in an earlier exercise:

public class Order
{
	private String code;
	
	public String getCode ()
	{
		return (String) this.code.clone (); 
	}
	
	private java.util.List<Product> products;
	
	public java.util.List<Product> getProducts ()
	{
		return this.products;
	}
	
	public Order () 
	{
		/* create reference code and an ArrayList for the products ordered. */
	}
}

Once the order reference code has been created, in the constructor, it really must never be changed because it will be used as the key for finding the order again. So the getter for that code returns a clone of the code, not a reference to the code itself. There is a further issue about this code which we will return to in a moment.

For the list of products ordered: should we return the list or a copy of it? NEITHER. There should not be a getter method for the list at all. This raises another very important OO principle: do not have a dog and bark yourself!

Why have a dog and bark yourself?

Don't ask for the information you need, in order to do the work yourself. Ask the object that has the information to do the work for you.

Don't ask the order object to give you all its data, for you to set or modify. Instead ask the order object to make or change the order.

In other words Order should be like our Stack: provide methods suitable for adding or removing products from an order but completely hide the implementation details. The outside world should not care what kind of data structure is used - and that makes it easier to change in future if it should prove necessary.

The dog knows how to bark much better than you do!

OrderCode

Let's look again at that order reference code. It is a String but it has a particular structure. Within a particular company's system order codes will have a certain number of characters, some might be letters, some digits, with a definite pattern. If we just use String as the type, where will you put the Java code for formatting it the right way?

It is a candidate for a new class: OrderCode which, like the dog, knows all about how to control itself.

So should OrderCode subclass String? If you try it, or look closely at the API documentation, you will find that is not possible: String is declared as final. The reason is to prevent people subverting the principle that strings are immutable.

So even though OrderCode is a String it will have to use composition.

It will be used as a key, so it is really important to override hashCode() and therefore also equals(). We need clone() too, so that Order.getCode() can return a reference to a copy of the code, not to the real thing.

Like String, OrderCode should be immutable: provide no setter for it and make it final.

(A curious fact that you might soon find useful: String is not cloneable.)

Then our revised Order class should look like this:

public class Order
{
	private OrderCode code;
	
	public OrderCode getCode ()
	{
		return (OrderCode) this.code.clone (); 
	}

	private java.util.List<Product> products;
	// No getter
	
	public Order () 
	{
		this.code = new OrderCode (); // Which knows how to create a unique code
		products = new java.util.ArrayList<Product> (); 
	}
	
	public void addProduct (Product p) { ... }
	
	public void removeProduct (Product p) { ... }
	
	/** Get code, list of product codes ordered */
	public String toString () { ... }
	
	// And probably some other useful methods:
	
	/** Get code, list of products ordered, as an HTML table */
	public String toHTML () { ... }
	
	public Amount getTotalCost () { ... }
	// Amount would also have to be defined: remember that financial systems must use
	// java.math.BigDecimal for accuracy, not float or double
}
Next page | Contents page |