• No results found

Interface Versus Implementation

Every class X exposes an interface (a protocol consisting of constructors, methods, and [possibly] fields that are made available to objects created from other classes for use in creating and communicating with X’s objects). X also provides an implementation (the code within exposed methods along with optional helper methods and optional

supporting fields that should not be exposed) that codifies the interface. Helper methods are methods that assist exposed methods and should not be exposed.

When designing a class, your goal is to expose a useful interface while hiding details of that interface’s implementation. You hide the implementation to prevent developers from accidentally accessing parts of your class that do not belong to the class’s interface, so that you are free to change the implementation without breaking client code. Hiding the implementation is often referred to as information hiding. Furthermore, many developers consider implementation hiding to be part of encapsulation.

Java supports implementation hiding by providing four levels of access control, where three of these levels are indicated via a reserved word. You can use the following access control levels to control access to fields, methods, and constructors, and two of these levels to control access to classes:

Public: A field, method, or constructor that is declared public is accessible from anywhere. Classes can be declared public as well. Protected: A field, method, or constructor that is declared protected is accessible from all classes in the same package as the member’s class as well as subclasses of that class, regardless of package. (I will discuss packages in Chapter 4.)

Private: A field, method, or constructor that is declared private cannot be accessed from beyond the class in which it is declared.

Package-private: In the absence of an access control reserved word, a field, method, or constructor is only accessible to classes within the same package as the member’s class. The same is true for non-public classes.

NOTE: A class that is declared public must be stored in a file with the same name. For example, a publicEmployee class must be stored in Employee.java. A source file can only contain one public class.

You will often declare your class’s instance fields to be private and provide special public instance methods for setting and getting their values. By convention, methods that set field values have names starting with set and are known as setters. Similarly, methods that get field values have names with get (or is, for Boolean fields) prefixes and are known as getters. Listing 2–35 demonstrates this pattern in the context of an Employee class declaration.

Listing 2–35. Separation of interface from implementation public class Employee

{

private String name;

public Employee(String name) {

setName(name); }

public void setName(String empName) {

name = empName; // Assign the empName argument to the name field. }

public String getName() {

return name; }

}

Listing 2–35 presents an interface consisting of the public constructor and public setter/getter methods. The implementation consists of the private name field and constructor/method code.

It might seem pointless to go to all this bother when you could simply omit private and access the name field directly. However, suppose you are told to introduce a new constructor that takes separate first and last name arguments, and new methods that set/get the employee’s first and last names into this class. Furthermore, suppose that it has been determined that the first and last names will be accessed more often than the entire name. Listing 2–36 reveals these changes.

Listing 2–36. Revising implementation without affecting existing interface class Employee

{

private String firstName; private String lastName; Employee(String name) {

setName(name); }

Employee(String firstName, String lastName) {

setName(firstName + " " + lastName); }

void setName(String name) {

// Assume that the first and last names are separated by a // single space character. indexOf() locates a character in a // string; substring() returns a portion of a string.

setFirstName(name.substring(0, name.indexOf(' '))); setLastName(name.substring(name.indexOf(' ')+1)); }

String getName() {

return getFirstName() + " " + getLastName(); }

void setFirstName(String empFirstName) { firstName = empFirstName; } String getFirstName() { return firstName; }

void setLastName(String empLastName) { lastName = empLastName; } String getLastName() { return lastName; } }

Listing 2–36 reveals that the name field has been removed in favor of new firstName and lastName fields, which were added to improve performance. Because setFirstName() and setLastName() will be called more frequently than setName(), and because getFirstName() and getLastName() will be called more frequently than getName(), it is faster (in each case) to have the first two methods set/get firstName’s and lastName’s values rather than having to merge either value into/extract this value from name’s value. Listing 2–36 also reveals setName() calling setFirstName() and setLastName(), and getName() calling getFirstName() and getLastName(), rather than directly accessing the firstName and lastName fields. Although avoiding direct access to these fields is not necessary in this example, imagine another implementation change that adds more

code to setFirstName(), setLastName(), getFirstName(), and getLastName(); not calling these methods will result in the new code not executing.

Client code (code that instantiates and uses a class, such as Employee) will not break when Employee’s implementation changes from that shown in Listing 2–35 to that shown in Listing 2–36, because the original interface remains intact, although the interface has been extended. This lack of breakage results from hiding Listing 2–35’s implementation, especially the name field.

TIP: Get into the habit of developing useful interfaces while hiding implementations because it will save you much trouble when maintaining your classes.

Objects

You previously learned that objects are instantiated from classes. You also discovered Java’s new operator in Table 2–3, and observed brief examples of this operator’s usage in Listings 2–32 and 2–34. This section explores object and array creation via new, and also focuses on accessing fields, calling methods, and garbage collection.