Chapter 8. Object-Oriented Techniques

8.0 Introduction

Java is an object-oriented (OO) language in the tradition of Simula-67, SmallTalk, and C++. It borrows syntax from C++ and ideas from SmallTalk. The Java API has been designed and built on the OO model. Design Patterns (see the book of the same name), such as Factory and Delegate, are used throughout; an understanding of these patterns will help you better understand the use of the API and improve the design of your own classes.

Advice, or Mantras

There are any number of short bits of advice that I could give. A few recurring themes arise when learning the basics of Java, and can usefully be reviewed when learning more Java.

Use the API

I can’t say this often enough. A lot of the things you need to do have already been done by the good folks who develop the standard Java library (and third-party libraries). And this grows with every release. Learning the API well is a good grounds for avoiding that deadly “reinventing the flat tire” syndrome—coming up with a second-rate equivalent of a first-rate product that was available to you the whole time. In fact, part of this book’s mission is to prevent you from reinventing what’s already there. One example of this is the Collections API in java.util, discussed in Chapter 7. The Collections API has a high degree of generality and regularity, so there is often no need to invent your own data structuring code.

Exceptions to the rule

There is one exception to the “use the API” rule: the clone() method in java.lang.Object should generally not be used. If you need to copy an object, just write a copy method, or a “copy constructor.” Joshua Bloch’s arguments against the clone() method in the book Effective Java (Addison-Wesley) are persuasive, and should be read by any dedicated Java programmer. While you’re at it, read that whole book.

Another exception is the finalize() method in java.lang.Object(). Don’t use it. It has been deprecated since Java 9, because it isn’t guaranteed to be invoked, but because it might get invoked, it will cause your dead objects not to be garbage collected, resulting in a memory leak. If you need some kind of cleanup, you must take responsibility for defining a method and invoking it before you let any object of that class go out of reference. You might call such a method cleanUp(). For application-level cleanup, see https://darwinsys.com/java/shutdownhook.html.

Generalize

There is a trade-off between generality (and the resulting reusability), which is emphasized here, and the convenience of application specificity. If you’re writing one small part of a very large application designed according to OO design techniques, you’ll have in mind a specific set of use cases. On the other hand, if you’re writing “toolkit-style” code, you should write classes with few assumptions about how they’ll be used. Making code easy to use from a variety of programs is the route to writing reusable code.

Read and write javadoc

You’ve no doubt looked at the Java online documentation in a browser, in part because I just told you to learn the API well. Do you think Sun/Oracle hired millions of tech writers to produce all that documentation? No. That documentation exists because the developers of the API took the time to write javadoc comments, those funny /** comments you’ve seen in code. So, one more bit of advice: use javadoc. The standard JDK provides a good, standard mechanism for API documentation. And use it as you write the code—don’t think you’ll come back and write it in later. That kind of tomorrow never comes.

See Recipe 15.2 for details on using javadoc.

Use subclassing and delegation

Use subclassing. But don’t overuse subclassing. It is one of the best ways not only for avoiding code duplication, but for developing software that works. See any number of good books on the topic of object-oriented design and programming for more details.

There are several alternatives. One alternative to subclassing is delegation. Think about “is-a” versus “has-a.” For example, instead of subclassing NameAndAddress to make BusinessPartner and Customer, make BusinessPartner and Customer have instances of NameAndAddress. That is a clearer structure; having BusinessPartner be a NameAndAddressjust because the partner has a name and address would not make sense. And delegation also makes it easier for a Customer to have both a billing address and a shipping address. Another alternative is aspect-oriented programming (AOP), which allows you to “bolt on” extra functionality from the outside of your classes. AOP is provided by the Java EE using EJB Interception, and by the Spring Framework AOP mechanism.

Use Design Patterns

In the Preface, I mentioned Design Patterns (Addison-Wesley) as one of the Very Important Books on object-oriented programming. Often called the “Gang of Four” (GoF) book for its four authors, it provides a powerful catalog of things that programmers often reinvent. Some people find the GoF book to be somewhat academic in tone; a less-formal presentation on patterns is the O’Reilly book +Head First Design Patterns_; this covers the same two dozen patterns as the GoF book. A Design Pattern provides a statement of a problem and its solution(s), rather like the present book, but generally at a higher level of abstraction. It is as important for giving a standard vocabulary of design as it is for its clear explanations of how the basic patterns work and how they can be implemented.

Table 8-1 shows some example uses of Design Patterns in the standard API.

Table 8-1. Design Patterns in the JavaSE API
Pattern name Meaning Examples in Java API

Command

Encapsulate requests, allowing queues of requests, undoable operations, etc.

javax.swing.Action; javax.swing.undo.UndoableEdit

Decorator

One class “decorates” another

Swing Borders

Factory Method

One class makes up instances for you, controlled by subclasses

getInstance (in Calendar, Format, Locale…); SocketFactory; RMI InitialContext

Iterator

Loop over all elements in a collection, visiting each exactly once

Iterator; older Enumeration; java.sql.ResultSet

Model-View-Controller

Model represents data; View is what the user sees; Controller responds to user requests

ActionListener and friends; Observer/Observable; used internally by all visible Swing components

Proxy

One object stands in for another

RMI, AOP, Dynamic Proxy

Singleton

Only one instance may exist

java.lang.Runtime, java.awt.Toolkit

I have written articles on the State, Proxy, Command, Decorator, and Visitor patterns for Oracle Java Magazine.

8.1 Object Methods: Formatting Objects with toString(), Comparing with Equals

Problem

You want your objects to have a useful default format, and to behave themselves when placed in Collections classes.

Solution

There are four overridable methods inherited from java.lang.Object; of these, toString() provides default formatting, while equals() and hashCode() provide equality testing and efficient usage in Map implementations. The fourth, clone(), is not recommended for general use.

Discussion

toString()

Whenever you pass an object to System.out.println() or any equivalent method, or involve it in string concatenation, Java automatically calls its toString() method. Java “knows” that every object has a toString() method because java.lang.Object has one and all classes are ultimately subclasses of Object. The default implementation, in java.lang.Object, is neither pretty nor interesting: it just prints the class name, an @ sign, and the object’s hashCode() value. For example, if you run this code:

public class ToStringWithout {
    int x, y;

    /** Simple constructor */
    public ToStringWithout(int anX, int aY) {
        x = anX; y = aY;
    }

    /** Main just creates and prints an object */
    public static void main(String[] args) {
        System.out.println(new ToStringWithout(42, 86));
    }
}

you might see this uninformative output:

ToStringWithout@990c747b

To make it print better, you should provide an implementation of toString() that prints the class name and some of the important states in all but the most trivial classes. This gives you formatting control in println(), in debuggers, and anywhere your objects get referred to in a String context. Here is the previous program rewritten with a toString() method:

public class ToStringWith {
    int x, y;

    /** Simple constructor */
    public ToStringWith(int anX, int aY) {
        x = anX; y = aY;
    }

    @Override
    public String toString() {
        return "ToStringWith[" + x + "," + y + "]";
    }

    /** Main just creates and prints an object */
    public static void main(String[] args) {
        System.out.println(new ToStringWith(42, 86));
    }
}

This version produces the more useful output:

ToStringWith[42,86]

This example uses String concatenation, but you may also want to use String.format() or StringBuilder; see Chapter 3.

hashCode() and equals()

To ensure your classes work correctly when any client code calls equals() or when these objects are stored in Map or other Collection classes, outfit your class with an equals() and hashCode() method.

How do you determine equality? For arithmetic or Boolean operands, the answer is simple: you test with the equals operator (==). For object references, though, Java provides both == and the equals() method inherited from java.lang.Object. The equals operator can be confusing because it simply compares two object references to see if they refer to the same object. This is not the same as comparing the values of the objects themselves.

The inherited equals() method is also not as useful as you might imagine. Some people seem to start their lives as Java developers thinking that the default equals() magically does some kind of detailed, field-by-field or even binary comparison of objects. But it does not compare fields! It just does the simplest possible thing: it returns the value of an == comparison on the two objects involved! So, for any value classes you write, you probably have to write an equals method.1 Note that both the equals and hashCode methods are used by Maps or hashes (such as HashMap; see Recipe 7.9). So if you think somebody using your class might want to create instances and put them into a Map, or even compare your objects, you owe it to them (and to yourself!) to implement both equals() and hashCode(), and to implement them properly.

Most IDEs know how to generate a correct equals() and hashCode() method, but it’s worth your while to understand what these are doing, for the occasional case where you need to “tweak” the generated code. The Eclipse IDE (see Recipe 1.3), for example, offers a Source menu item Generate hashCode() and equals()—it will only do both at the same time, not let you generate equals() without hashCode() nor vice versa.

Here are the rules for a correct equals() method:

It is reflexive

x.equals(x) must be true.

It is symmetrical

x.equals(y) must be true if and only if y.equals(x) is also true.

It is transitive

If x.equals(y) is true and y.equals(z) is true, then x.equals(z) must also be true.

It is idempotent (repeatable)

Multiple calls on x.equals(y) return the same value (unless state values used in the comparison are changed, as by calling a set method).

It is cautious

x.equals(null) must return false rather than accidentally throwing a NullPointerException.

In addition, beware of one common mistake: the argument to equals() must be declared as java.lang.Object, not the class it is in; this is so that polymorphism will work correctly (some classes may not have an equals() method of their own). To prevent this mistake, the @Override annotation is usually added to the equals() override, as mentioned in Recipe 15.3.

Here is a class that endeavors to implement these rules:

public class EqualsDemo {
    private int int1;
    private SomeClass obj1;

    /** Constructor */
    public EqualsDemo(int i, SomeClass o) {
        int1 = i;
        if (o == null) {
            throw new IllegalArgumentException("Data Object may not be null");
        }
        obj1 = o;
    }

    /** Default Constructor */
    public EqualsDemo() {
        this(0, new SomeClass());
    }

    /** Demonstration "equals" method */
    @Override
    public boolean equals(Object o) {
        if (o == this)                    1
            return true;

        if (o == null)                    2
            return false;

        // Of the correct class?
        if (o.getClass() != EqualsDemo.class) 3
            return false;

        EqualsDemo other = (EqualsDemo)o; // OK, cast to this class

        // compare field-by-field         4
        if (int1 != other.int1)           // compare primitives directly
            return false;
        if (!obj1.equals(other.obj1))     // compare objects using their equals
            return false;
        return true;
    }

    // ...
1

Optimization: if same object, true by definition.

2

If other object null, false by definition.

3

Compare class descriptors using !=; see following paragraph.

4

Optimization: compare primitives first. May or may not be worthwhile; may be better to order by those most likely to differ—depends on the data and the usage.

Another common mistake to avoid: note the use of class descriptor equality (i.e., o.getClass() != EqualsDemo.class) to ensure the correct class, rather than via instanceof, as is sometimes erroneously done. The reflexive requirement of the equals() method contract pretty much makes it impossible to compare a subclass with a superclass correctly, so we now use class equality (see Chapter 17, Reflection, or “A Class Named Class” for details on the class descriptor).

Here is a basic JUnit test (see Recipe 1.10) for the EqualsDemo class:

/** Some JUnit test cases for EqualsDemo.
 * Writing a full set is left as "an exercise for the reader".
 */
public class EqualsDemoTest {

    /** an object being tested */
    EqualsDemo d1;
    /** another object being tested */
    EqualsDemo d2;

    /** Method to be invoked before each test method */
    @Before
    public void setUp() {
        d1 = new EqualsDemo();
        d2 = new EqualsDemo();
    }

    @Test
    public void testSymmetry() {
        assertTrue(d1.equals(d1));
    }

    @Test
    public void testSymmetric() {
        assertTrue(d1.equals(d2) && d2.equals(d1));
    }

    @Test
    public void testCaution() {
        assertFalse(d1.equals(null));
    }
}

With all that testing, what could go wrong? Well, some things still need care. What if the object is a subclass of EqualsDemo? We should test that it returns false in this case.

What else could go wrong? Well, what if either obj1 or other.obj1 is null? You might have just earned a nice shiny new NullPointerException. So you also need to test for any possible null values. Good constructors can avoid these NullPointerExceptions, as I’ve tried to do in EqualsDemo, or else test for them explicitly.

Finally, you should never override equals() without also overriding hashCode(), and the same fields must take part in both computations.

hashCode()

The hashCode() method is supposed to return an int that should uniquely identify any set of values in objects of its class.

A properly written hashCode() method will follow these rules:

It is repeatable.

hashCode(x) must return the same int when called repeatedly, unless set methods have been called.

It is consistent with equality.

If x.equals(y), then x.hashCode() must == y.hashCode().

Distinct objects should produce distinct hashCodes

If !x.equals(y), it is not required that x.hashCode() != y.hashCode(), but doing so may improve performance of hash tables (i.e., hashes may call hashCode() before equals()).

The default hashCode() on the standard JDK returns a machine address, which conforms to the first rule. Conformance to the second and third rules depends, in part, on your equals() method. Here is a program that prints the hashcodes of a small handful of objects:

public class PrintHashCodes {

    /** Some objects to hashCode() on */
    protected static Object[] data = {
        new PrintHashCodes(),
        new java.awt.Color(0x44, 0x88, 0xcc),
        new SomeClass()
    };

    public static void main(String[] args) {
        System.out.println("About to hashCode " + data.length + " objects.");
        for (int i=0; i<data.length; i++) {
            System.out.println(data[i].toString() + " --> " +
                data[i].hashCode());
        }
        System.out.println("All done.");
    }
}

What does it print?

> javac -d . oo/PrintHashCodes.java
> java oo.PrintHashCodes
About to hashCode 3 objects.
PrintHashCodes@982741a0 --> -1742257760
java.awt.Color[r=68,g=136,b=204] --> -12285748
SomeClass@860b41ad --> -2046082643
All done.
>

The hashcode value for the Color object is interesting. It is actually computed as something like:

alpha<<24 + r<<16 + g<<8 + b

In this formula, r, g, and b are the red, green, and blue components, respectively, and alpha is the transparency. Each of these quantities is stored in 8 bits of a 32-bit integer. If the alpha value is greater than 128, the “high bit” in this word—having been set by shifting into the sign bit of the word—causes the integer value to appear negative when printed as a signed integer. Hashcode values are of type int, so they are allowed to be negative.

Difficulties and Alternatives to Clone

The java.util.Observable class (designed to implement the Model-View-Controller pattern with AWT or Swing applications) contains a private Vector but no clone method to deep-clone it. Thus, Observable objects cannot safely be cloned, ever!

This and several other issues around clone()—such as the uncertainty of whether a given clone() implementation is deep or shallow—suggest that clone() was not as well thought out as might be. An alternative is simply to provide a “copy constructor” or similar method:

public class CopyConstructorDemo {
    public static void main(String[] args) {
        CopyConstructorDemo object1 = new CopyConstructorDemo(123, "Hello");
        CopyConstructorDemo object2 = new CopyConstructorDemo(object1);
        if (!object1.equals(object2)) {
            System.out.println("Something is terribly wrong...");
        }
        System.out.println("All done.");
    }

    private int number;
    private String name;

    /** Default constructor */
    public CopyConstructorDemo()  {
    }

    /** Normal constructor */
    public CopyConstructorDemo(int number, String name)  {
        this.number = number;
        this.name = name;
    }

    /** Copy Constructor */
    public CopyConstructorDemo(CopyConstructorDemo other)  {
        this.number = other.number;
        this.name = other.name;
    }
    // hashCode() and equals() not shown

8.2 Using Inner Classes

Problem

You need to write a private class, or a class to be used in one other class at most.

Solution

Use a nonpublic class or an inner class.

Discussion

A nonpublic class can be written as part of another class’s source file, but not inside that class. An inner class is Java terminology for a class defined inside another class. Inner classes were first popularized with early Java for use as event handlers for GUI applications, but they have a much wider application.

Inner classes can, in fact, be constructed in several contexts. An inner class defined as a member of a class can be instantiated anywhere in that class. An inner class defined inside a method can be referred to later only in the same method. Inner classes can also be named or anonymous. A named inner class has a full name that is compiler-dependent; the standard JVM uses a name like MainClass$InnerClass for the resulting file. An anonymous inner class, similarly, has a compiler-dependent name; the JVM uses MainClass$1, MainClass$2, and so on.

These classes cannot be instantiated in any other context; any explicit attempt to refer to, say, OtherMainClass$InnerClass, is caught at compile time:

main/src/main/java/oo/AllClasses.java

public class AllClasses {
    public class Data {    1
        int x;
        int y;
    }
    public void getResults() {
        JButton b = new JButton("Press me");
        b.addActionListener(new ActionListener() { 2
            public void actionPerformed(ActionEvent evt) {
                Data loc = new Data();
                loc.x = ((Component)evt.getSource()).getX();
                loc.x = ((Component)evt.getSource()).getY();
                System.out.println("Thanks for pressing me");
            }
        });
    }
}

/** Class contained in same file as AllClasses, but can be used
 * (with a warning) in other contexts.
 */
class AnotherClass {                    3
    // methods and fields here...
    AnotherClass() {
        // Inner class from above cannot be used here, of course
        // Data d = new Data();    // EXPECT COMPILE ERROR
    }
}
1

Inner class, can be used anywhere in class AllClasses.

2

Anonymous inner class syntax, using new with a type followed by (){, a class body, and }. Compiler will assign a name; class will extend or implement the given type, as appropriate.

3

Nonpublic class; can be used in the main class, and (with warning) in other classes.

One issue is that the inner class retains a reference to the outer class. If you want to avoid memory leaks if the inner class will be held for a longer time than the outer, you can make the inner class be static.

Inner classes implementing a single-method interface can be written in a much more concise fashion as lambda expressions (see Chapter 9).

Problem

You want to provide callbacks—that is, have unrelated classes call back into your code.

Solution

One way is to use a Java interface.

Discussion

An interface is a class-like entity that can contain only abstract methods and final fields. As we’ve seen, interfaces are used a lot in Java! In the standard API, the following are a few of the commonly used interfaces:

  • Runnable, Comparable, and Cloneable (in java.lang)

  • List, Set, Map, and Enumeration/Iterator (in the Collections API; see Chapter 7).

  • ActionListener, WindowListener, and others in the GUI layer.

  • Driver, Connection, Statement, and ResultSet in JDBC; see https://darwinsys.com/javadatase.

  • The “remote interface”—the contact between the client and the server—is specified as an Interface (in RMI, CORBA, and EJB)

Suppose we are generating a building management system. To be energy efficient, we want to be able to remotely turn off (at night and on weekends) such things as room lights and computer monitors, which use a lot of energy. Assume we have some kind of “remote control” technology. It could be a commercial version of BSR’s house-light control technology X10, it could be Bluetooth or 802.11—it doesn’t matter. What matters is that we have to be very careful what we turn off. It would cause great ire if we turned off computer processors automatically—people often leave things running overnight. It would be a matter of public safety if we ever turned off the building emergency lighting.2

So we’ve come up with the design shown in Figure 8-1.

jcb4 0801
Figure 8-1. Classes for a building management system

The code for these data classes is not shown (it’s pretty trivial) but it’s in the oo/interfaces directory of the online source. The top-level classes (i.e., BuildingLight and Asset) are abstract classes. You can’t instantiate them, because they don’t have any specific functionality. To ensure—both at compile time and at runtime—that we can never switch off the emergency lighting, we need only ensure that the class representing it, EmergencyLight, does not implement the PowerSwitchable interface.

Note that we can’t very well use direct inheritance here. No common ancestor class includes both ComputerMonitor and RoomLights that doesn’t also include ComputerCPU and EmergencyLight. Use interfaces to define functionality in unrelated classes.

How we use these is demonstrated by the BuildingManagement class; this class is not part of the hierarchy shown in Figure 8-1, but uses a collection of Asset objects from that hierarchy.

Items that can’t be switched must nonetheless be in the database, for various purposes (auditing, insurance, etc.). In the method that turns things off, the code is careful to check whether each object in the database is an instance of the PowerSwitchable interface. If so, the object is casted to PowerSwitchable so that its powerDown() method can be called. If not, the object is skipped, thus preventing any possibility of turning out the emergency lights or shutting off a machine that is busy running Seti@Home, downloading a big MP3 playlist, or performing system backups. The following code shows this set of classes in action:

public class BuildingManagement {

    List<Asset> things = new ArrayList<>();

    /** Scenario: goodNight() is called from a timer Thread at 2200, or when
     * we get the "shutdown" command from the security guard.
     */
    public void goodNight() {
        things.forEach(obj -> {
            if (obj instanceof PowerSwitchable)
                ((PowerSwitchable)obj).powerDown();
            });
    }

    // tag::functional[]
    public void goodNightFunctional() {
        things.stream().filter(obj -> obj instanceof PowerSwitchable)
            .forEach(obj -> ((PowerSwitchable)obj).powerDown());
    }
    // end::functional[]

    // goodMorning() would be similar, but call each one's powerUp().

    /** Add a Asset to this building */
    public void add(Asset thing) {
        System.out.println("Adding " + thing);
        things.add(thing);
    }

    /** The main program */
    public static void main(String[] av) {
        BuildingManagement b1 = new BuildingManagement();
        b1.add(new RoomLights(101));    // control lights in room 101
        b1.add(new EmergencyLight(101));    // and emerg. lights.
        // add the computer on desk#4 in room 101
        b1.add(new ComputerCPU(10104));
        // and its monitor
        b1.add(new ComputerMonitor(10104));

        // time passes, and the sun sets...
        b1.goodNight();
    }
}

When you run this program, it shows all the items being added, but only the PowerSwitchable ones being switched off:

> java oo.interfaces.BuildingManagement
Adding RoomLights@2dc77f32
Adding EmergencyLight@2e3b7f32
Adding ComputerCPU@2e637f32
Adding ComputerMonitor@2f1f7f32
Dousing lights in room 101
Dousing monitor at desk 10104
>

8.3 Polymorphism/Abstract Methods

Problem

You want each of a number of subclasses to provide its own version of one or more methods.

Solution

Make the method abstract in the parent class; this makes the compiler ensure that each subclass implements it.

Discussion

A hypothetical drawing program uses a Shape subclass for anything that is drawn. Shape has an abstract method called computeArea() that computes the exact area of the given shape:

public abstract class Shape {
    protected int x, y;
    public abstract double computeArea( );
}

A Rectangle subclass, for example, has a computeArea() that multiplies width times height and returns the result:

public class Rectangle extends Shape {
    double width, height;
    public double computeArea( ) {
        return width * height;
    }
}

A Circle subclass returns πr2:

public class Circle extends Shape {
    double radius;
    public double computeArea( ) {
        return Math.PI * radius * radius;
    }
}

This system has a high degree of generality. In the main program, we can iterate over a collection of Shape objects and—here’s the real beauty—call computeArea() on any Shape subclass object without having to worry about what kind of shape it is. Java’s polymorphic methods automatically call the correct computeArea( ) method in the class of which the object was originally constructed:

main/src/main/java/oo//shapes/ShapeDriver.java

/** Part of a main program using Shape objects */
public class ShapeDriver {

    Collection<Shape> allShapes;    // created in a Constructor, not shown

    /** Iterate over all the Shapes, getting their areas;
     * this cannot use the Java 8 Collection.forEach because the
     * variable total would have to be final, which would defeat the purpose :-)
     */
    public double totalAreas() {
        double total = 0.0;
        for (Shape s : allShapes) {
            total += s.computeArea();
        }
        return total;
    }

Polymorphism is a great boon for software maintenance: if a new subclass is added, the code in the main program does not change. Further, all the code that is specific to, say, polygon handling, is all in one place: in the source file for the Polygon class. This is a big improvement over older languages, where type fields in a structure were used with case or switch statements scattered all across the software. Java makes software more reliable and maintainable with the use of polymorphism.

8.4 Using Typesafe Enumerations

Problem

You need to manage a small list of discrete values within a program.

Solution

Use the Java enum mechanism.

Discussion

To enumerate means to list all the values. You often know that a small list of possible values is all that’s wanted in a variable, such as the months of the year, the suits or ranks in a deck of cards, the primary and secondary colors, and so on. The C programming language provided an enum keyword:

enum  { BLACK, RED, ORANGE} color;

Java was criticized in its early years for its lack of enumerations, which many developers have wished for. Many have had to develop custom classes to implement the “typesafe enumeration pattern.”

But C enumerations are not “typesafe”; they simply define constants that can be used in any integer context. For example, this code compiles without warning, even on gcc 3 with -Wall (all warnings), whereas a C++ compiler catches the error:3

enum { BLACK, RED, ORANGE} color;
enum { READ, UNREAD } state;

/*ARGSUSED*/
int main(int argc, char *argv[]) {
        color = RED;
        color = READ; // In C this will compile, give bad results
        return 0;
}

To replicate this mistake in Java, one needs only to define a series of final int values; it will still not be typesafe. By typesafe I mean that you cannot accidentally use values other than those defined for the given enumeration. The definitive statement on the “typesafe enumeration pattern” is probably the version defined in Item 21 of Joshua Bloch’s book Effective Java (Addison-Wesley). All modern Java versions include enumerations in the language; it is no longer necessary to use the code from Bloch’s book. Bloch was one of the authors of the Typesafe Enumeration specification (enum keyword), so you can be sure that Java now does a good job of implementing his pattern. These enums are implemented as classes, subclassed (transparently, by the compiler) from the class java.lang.Enum. Unlike C, and unlike the “series of final int” implementation, Java typesafe enumerations:

  • Are printable (they print as the name, not as an underlying int implementation).

  • Are almost as fast as int constants, but the code is more readable.

  • Can be easily iterated over.

  • Utilize a separate namespace for each enum type, so you don’t have to prefix each with some sort of constant name, like ACCOUNT_SAVINGS, ACCOUNT_CHECKING, etc.

Enum constants are not compiled into clients, giving you the freedom to reorder the constants within your enum without recompiling the client classes. That does not mean you should, however; think about the case where objects that use them have been persisted, and the person designing the database mapping used the numeric values of the enums. Bad idea to reorder then!

Additionally, an enum type is a class, so it can, for example, implement arbitrary interfaces, and you can add constructors, fields, and methods to an enum class.

Compared to Bloch’s Typesafe Enum pattern in the book:

  • Java enums are simpler to use and more readable (those in the book require a lot of methods, making them cumbersome to write).

  • Enums can be used in switch statements.

So there are many benefits and few pitfalls.

The enum keyword is at the same level as the keyword class in declarations. That is, an enum may be declared in its own file with public or default access. It may also be declared inside classes, much like nested or inner classes (see Recipe 8.2). Media.java, shown in Example 8-1, is a code sample showing the definition of a typesafe enum.

Example 8-1. structure/Media.java
public enum Media {
    BOOK, MUSIC_CD, MUSIC_VINYL, MOVIE_VHS, MOVIE_DVD;
}

Notice that an enum class is a class; see what javap thinks of the Media class:

C:> javap Media
Compiled from "Media.java"
public class Media extends java.lang.Enum{
    public static final Media BOOK;
    public static final Media MUSIC_CD;
    public static final Media MUSIC_VINYL;
    public static final Media MOVIE_VHS;
    public static final Media MOVIE_DVD;
    public static final Media[] values( );
    public static Media valueOf(java.lang.String);
    public Media(java.lang.String, int);
    public int compareTo(java.lang.Enum);
    public int compareTo(java.lang.Object);
    static {};
}
C:>

Product.java, shown in Example 8-2, is a code sample that uses the Media enum.

Example 8-2. main/src/main/java/structure/Product.java
public class Product {
    String title;
    String artist;
    Media  media;

    public Product(String artist, String title, Media media) {
        this.title = title;
        this.artist = artist;
        this.media = media;
    }

    @Override
    public String toString() {
        switch (media) {
        case BOOK:
            return title + " is a book";
        case MUSIC_CD:
            return title + " is a CD";
        case MUSIC_VINYL:
            return title + " is a relic of the age of vinyl";
        case MOVIE_VHS:
            return title + " is on old video tape";
        case MOVIE_DVD:
            return title + " is on DVD";
        default:
            return title + ": Unknown media " + media;
        }
    }
}

In Example 8-3, MediaFancy shows how operations (methods) can be added to enumerations; the toString() method is overridden for the “book” value of this enum.

Example 8-3. main/src/main/java/structure/MediaFancy.java
/** An example of an enum with method overriding */
public enum MediaFancy {
    /** The enum constant for a book, with a method override */
    BOOK {
        public String toString() { return "Book"; }
    },
    /** The enum constant for a Music CD */
    MUSIC_CD,
    /** ... */
    MUSIC_VINYL,
    MOVIE_VHS,
    MOVIE_DVD;

    /** It is generally disparaged to have a main() in an enum;
     * please forgive this tiny demo class for doing so.
     */
    public static void main(String[] args) {
        MediaFancy[] data =  { BOOK, MOVIE_DVD, MUSIC_VINYL };
        for (MediaFancy mf : data) {
            System.out.println(mf);
        }
    }
}

Running the MediaFancy program produces this output:

Book
MOVIE_DVD
MUSIC_VINYL

That is, the Book values print in a “user-friendly” way compared to the default way the other values print. In real life you’d want to extend this to all the values in the enum.

Finally, EnumList, in Example 8-4, shows how to list all the possible values that a given enum can take on; simply iterate over the array returned by the enum class’s inherited values() method.

Example 8-4. structure/EnumList.java
public class EnumList {
    enum State {
        ON, OFF, UNKNOWN
    }
    public static void main(String[] args) {
        for (State i : State.values()) {
            System.out.println(i);
        }
    }
}

The output of the EnumList program is, of course:

ON
OFF
UNKNOWN

8.5 Avoiding NPEs with Optional

Problem

You worry about null references causing NullPointerException (NPE) in your code.

Solution

Use java.util.Optional

Discusssion

The developer who invented the notion of null pointers, and a key early contributor to our discipline, has described the null reference as “my billion-dollar mistake”. However, use of null is not going away anytime soon.

What we can do is make clear that we worry about them in certain contexts. For this purpose, Java 8 introduced the class java.util.Optional. The Optional is an object wrapper around a possibly-null object reference. The “Optional” wrapper has a long history; a similar construct is found in LLVM’s ADT, where its Optional describes itself in turn as “in the spirit of OCaml’s opt variant”.

Optionals can be created with one of the creational methods:

Optional.empty() returns an empty optional Optional.of(T obj) returns a non-empty optional containing the given value Optional.ofNullable(T obj) returns either an empty Optional or one containing the given value.

The basic operation of this class is to behave in one of two ways depending on whether it is full or empty. Optional objects are immutable so they cannot transition from one state to the other.

The simplest use is to invoke isEmpty() or its opposite isPresent() and use program logic to behave differently. Not much different from using an if statement to check for null, but it puts the choice in front of you, making it less likely that you’ll forget to check.

jshell> Optional<String> opt = Optional.of("What a day!");
opt ==> Optional[What a day!]

jshell> if (opt.isPresent()) {
   ...>     System.out.println("Value is " + opt.get());
   ...> } else {
   ...>     System.out.println("Value is not present.");
   ...> }
Value is What a day!

A better form would use the orElse method:

jshell> System.out.println("Value is " + opt.orElse("not present"));
Value is What a day!

A useful use case is that of passing values into methods. The object can be wrapped in an Optional either before it is passed to a method or after; the latter is useful when migrating from code that didn’t use Optional from the start. This Item demo might represent part of a shipments tracking program, a lending library manager, or anything else that has time-related data which might be missing.

Example 8-5. main/src/main/java/oo/OptionalDemo.java
        List.of(
            new Item("Item 1", LocalDate.now().plusDays(7)),
            new Item("Item 2")).
                forEach(System.out::println);
    static class Item {
        String name;
        Optional<LocalDate> dueDate;
        Item(String name) {
            this(name, null);
        }
        Item(String name, LocalDate dueDate) {
            this.name = name;
            this.dueDate = Optional.ofNullable(dueDate);
        }

        public String toString() {
            return String.format("%s %s", name,
                dueDate.isPresent() ?
                    "Item is due on " + dueDate.get() :
                    "Sorry, do not know when item is due");
        }
    }

There are methods that throw exceptions, that return null, and so on. Also methods for interacting with the Streams mechanism (see Recipe 9.4). A full list of Optional’s methods is at the start of the javadoc page.

8.6 Enforcing the Singleton Pattern

Problem

You want to be sure there is only one instance of your class in a given Java Virtual Machine, or at least within your application.

Solution

There are several methods of making your class enforce the Singleton Pattern

  • Enum implementation;

  • Having only a private constructor(s) and a getInstance() method;

  • Use a frameworks such as Spring or CDI (Recipe 8.8) configured to give Singleton-style instantiation of plain classes.

Discussion

It is often useful to ensure that only one instance of a class gets created, usually to funnel all requests for some resource through a single point. An example of a Singleton from the standard API is java.lang.Runtime ; you cannot create instances of Runtime, you simply ask for a reference by calling the static method Runtime.getRuntime(). Singleton is also an example of a design pattern that can be easily implemented. In all forms, the point of the singleton implementation is to provide an instance in which certain methods can run, typically to control access to some reasource.

The easiest implementation uses a Java enum to provide singletonness. The enum mechanism already guarantees that only one instance of each enum constant will exist in a given JVM context, so this technique piggy-backs on that.

Example 8-6. main/src/main/java/oo/EnumSingleton.java
public enum EnumSingleton {

    INSTANCE;

    // instance methods protected by singleton-ness would be here...

    /** A simple demo method */
    public String demoMethod() {
        return "demo";
    }
}

Using it is simple:

        // Demonstrate the enum method:
        EnumSingleton.INSTANCE.demoMethod();

The next easiest implementation consists of a private constructor and a field to hold its result, and a static accessor method with a name like getInstance() .

The private field can be assigned from within a static initializer block or, more simply, using an initializer. The getInstance() method (which must be public) then simply returns this instance:

public class Singleton {

    /**
     * Static Initializer is run before class is available to code, avoiding
     * broken anti-pattern of lazy initialization in instance method.
     * For more complicated construction, could use static block initializer.
     */
    private static Singleton instance = new Singleton();

    /** A private Constructor prevents any other class from instantiating. */
    private Singleton() {
        // nothing to do this time
    }

    /** Static 'instance' method */
    public static Singleton getInstance() {
        return instance;
    }

    // other methods protected by singleton-ness would be here...

    /** A simple demo method */
    public String demoMethod() {
        return "demo";
    }
}

Note that the method of using “lazy evaluation” in the getInstance() method (which is advocated in Design Patterns), is not necessary in Java because Java already uses “lazy loading.” Your Singleton class will probably not get loaded until its getInstance() is called, so there is no point in trying to defer the singleton construction until it’s needed by having getInstance() test the singleton variable for null and creating the singleton there.

Using this class is equally simple: simply get the instance reference, and invoke methods on it:

        // Demonstrate the codeBased method:
        Singleton.getInstance().demoMethod();

Some commentators believe that a code-based Singleton should also provide a public final clone() method that just throws an exception, to avoid subclasses that “cheat” and clone() the singleton. However, it is clear that a class with only a private constructor cannot be subclassed, so this paranoia does not appear to be necessary.

See Also

The Collections class in java.util has methods singletonList(), singletonMap(), and singletonSet(), which give out an immutable List, Map, or Set, respectively, containing only the one object that is passed to the method. This does not, of course, convert the object into a Singleton in the sense of preventing that object from being cloned or other instances from being constructed.

See page 127 of the original Design Patterns book.

8.7 Roll Your Own Exceptions

Problem

You’d like to use an application-specific exception class or two.

Solution

Go ahead and subclass Exception or RuntimeException.

Discussion

In theory, you could subclass Throwable directly, but that’s considered rude. You normally subclass Exception (if you want a checked exception) or RuntimeException (if you want an unchecked exception). Checked exceptions are those that an application developer is required to catch or “throw upward” by listing them in the throws clause of the invoking method.

When subclassing either of these, it is customary to provide at least these constructors:

  • A no-argument constructor

  • A one-string argument constructor

  • A two argument constructor—a string message and a Throwable “cause”

The “cause” will appear if the code receiving the exception performs a stack trace operation on it, with the prefix “Root Cause is” or similar. Example 8-7 shows these three constructors for an application-defined exception, ChessMoveException:.

Example 8-7. main/src/main/java/oo/ChessMoveException.java
/** A ChessMoveException is thrown  when the user makes an illegal move. */
public class ChessMoveException extends Exception {

    private static final long serialVersionUID = 802911736988179079L;

    public ChessMoveException () {
        super();
    }

    public ChessMoveException (String msg) {
        super(msg);
    }

    public ChessMoveException(String msg, Exception cause) {
        super(msg, cause);
    }
}

See Also

The javadoc documentation for Exception lists a large number of subclasses; you might look there first to see if there is one you can use.

8.8 Using Dependency Injection

Problem

You want to avoid excessive coupling between classes, and you want to avoid excessive code dedicated to object creation/lookup.

Solution

Use a Dependency Injection Framework.

Discussion

A Dependency Injection Framework allows you to have objects “passed in” to your code instead of making you either create them explicitly (which ties your code to the implementing class name, since you’re calling the constructor) or looking for them (which requires use of a possibly cumbersome lookup API, such as JNDI, the Java Naming and Directory Interface).

Three of the best-known Dependency Injection Frameworks are the Spring Framework, the Java Enterprise Edition’s Context and Depency Injection (CDI), and Google Guice. Suppose we have three classes, a Model, a View, and a Controller, implementing the traditional MVC pattern. Given that we may want to have different versions of some of these, especially the View, we’ll define Java interfaces for simple versions of the Model and View, shown in the following code:

Example 8-8. MVC Model Interface
public interface Model {
	String getMessage();
}
Example 8-9. main/src/main/java/di/View.java - MVC View Interface
public interface View {

    void displayMessage();

}

The implementations of these are not shown, because they’re so trivial, but they are online. The Controller in this example is a main program, no interface needed. First, a version of the main program not using Dependency Injection. Obviously the View requires the Model, to get the data to display:

main/src/main/java/di/ControllerTightlyCoupled.java

public class ControllerTightlyCoupled {

    public static void main(String[] args) {
        Model m = new SimpleModel();
        ConsoleViewer v = new ConsoleViewer();
        v.setModel(m);
        v.displayMessage();
    }
}

Here we have four tasks to undertake:

  1. Create the Model.

  2. Create the View.

  3. Tie the Model into the View.

  4. Ask the View to display some data.

Now a version using Dependency Injection:

main/src/main/java/di/spring/MainAndController.java - Spring Controller

public class MainAndController {

    public static void main(String[] args) {
        ApplicationContext ctx =
            new AnnotationConfigApplicationContext("di.spring");
        View v = ctx.getBean("myView", View.class);
        v.displayMessage();
        ((AbstractApplicationContext) ctx).close();
    }
}

In this version, we have only three tasks:

  1. Set up the Spring “context,” which provides the dependency injection framework.

  2. Get the View from the context; it already has the Model set into it!

  3. Ask the View to display some data.

Furthermore, we don’t depend on particular implementations of the interface.

How does Spring know to “inject” or provide a Model to the View? And how does it know what code to use for the View? There might be multiple implementations of the View interface. Of course we have to tell it these things, which we’ll do here with annotations:

@Named("myView")
public class ConsoleViewer implements View {

    Model messageProvider;

    @Override
    public void displayMessage() {
        System.out.println(messageProvider.getMessage());
    }

    @Resource(name="myModel")
    public void setModel(Model messageProvider) {
        this.messageProvider = messageProvider;
    }

}

While Spring has provided its own annotations, it will also accept the Java standard @javax.annotation.Resource annotation for injection and @java.inject.Named to specify the injectee.

Due to the persistence of information on the Web, if you do a web search for Spring Injection, you will probably find zillions of articles that refer to the older Spring 2.x way of doing things, which is to use an XML configuration file. You can still use this, but modern Spring practice is generally to use Java annotations to configure the dependencies.

Annotations are also used in the Java Enterprise Edition Contexts and Dependency Injection (CDI). Although this is most widely used in web applications, we’ll reuse the same example, using the open source “Weld” implementation of CDI. CDI is quite a bit more powerful than Spring’s DI; because in CDI we don’t even need to know the class from which a resource is being injected, we don’t even need the interfaces from the Spring example! First, the “Controller” or main program, which requires a Weld-specific import or two because CDI was originally designed for use in enterprise applications:

public class MainAndController {
    public static void main(String[] args) {
        final Instance<Object> weldInstance = new Weld().initialize().instance();
        weldInstance.select(ConsoleViewer.class).get().displayMessage();
    }
}

The View interface is shared between both implementations. The ConsoleViewer implementation is similar too, except it isn’t coupled to the Model; it just asks to have a String injected. In this simple example there is only one String in the application; in a larger app you would need one additional annotation to specify which string to inject. Here is the CDI ConsoleViewer:

public class ConsoleViewer implements View {
    @Inject @MyModel
    private String message;

    @Override
    public void displayMessage() {
        System.out.println(message);
    }
}

Where does the injected String come from? From the Model, as before:

main/src/main/java/di/cdi/ModelImpl.java

public class ModelImpl {

    public @Produces @MyModel String getMessage(InjectionPoint ip)
        throws IOException {

        ResourceBundle props = ResourceBundle.getBundle("messages");
        return props.getString(
            ip.getMember().getDeclaringClass().getSimpleName() + "." +
            ip.getMember().getName());
    }
}

See Also

Spring DI, Java EE CDI, and Guice all provide powerful “dependency injection.” Spring’s is more widely used; Java EE’s has the same power and is built into every EE container.. All three can be used standalone or in a web application, with minor variations. In the EE, Spring provides special support for web apps, and in EE containers, CDI is already set up so the first statement in the CDIMain example is not needed in an EE app. There are many books on Spring. One book specifically treats Weld: JBoss Weld CDI for Java Platform by Ken Finnegan (O’Reilly).

8.9 Program: Plotter

Not because it is very sophisticated, but because it is simple, this program serves as an example of some of the things we’ve covered in this chapter, and also, in its subclasses, provides a springboard for other discussions. This class describes a series of old-fashioned (i.e., common in the 1970s and 1980s) pen plotters. A pen plotter, in case you’ve never seen one, is a device that moves a pen around a piece of paper and draws things. It can lift the pen off the paper or lower it, and it can draw lines, letters, and so on. Before the rise of laser printers and ink-jet printers, pen plotters were the dominant means of preparing charts of all sorts, as well as presentation slides (this was, ah, well before the rise of programs like Harvard Presents and Microsoft PowerPoint). Today, few, if any, companies still manufacture pen plotters, but I use them here because they are simple enough to be well understood from this brief description. Today’s “3-D Printers” may be thought of as representing a resurgence of the pen plotter with just one additional axis of motion. And a fancier pen.

I’ll present a high-level class that abstracts the key characteristics of a series of such plotters made by different vendors. It would be used, for example, in an analytical or data-exploration program to draw colorful charts showing the relationships found in data. But I don’t want my main program to worry about the gory details of any particular brand of plotter, so I’ll abstract into a Plotter class, whose source is as follows:

main/src/main/java/plotter/Plotter.java

/**
 * Plotter abstract class. Must be subclassed
 * for X, DOS, Penman, HP plotter, etc.
 *
 * Coordinate space: X = 0 at left, increases to right.
 *        Y = 0 at top, increases downward (same as AWT).
 *
 * @author    Ian F. Darwin
 */
public abstract class Plotter {
    public final int MAXX = 800;
    public final int MAXY = 600;
    /** Current X co-ordinate (same reference frame as AWT!) */
    protected int curx;
    /** Current Y co-ordinate (same reference frame as AWT!) */
    protected int cury;
    /** The current state: up or down */
    protected boolean penUp;
    /** The current color */
    protected int penColor;

    Plotter() {
        penUp = true;
        curx = 0; cury = 0;
    }
    abstract void rmoveTo(int incrx, int incry);
    abstract void moveTo(int absx, int absy);
    abstract void penUp();
    abstract void penDown();
    abstract void penColor(int c);

    abstract void setFont(String fName, int fSize);
    abstract void drawString(String s);

    /* Concrete methods */

    /** Draw a box of width w and height h */
    public void drawBox(int w, int h) {
        penDown();
        rmoveTo(w, 0);
        rmoveTo(0, h);
        rmoveTo(-w, 0);
        rmoveTo(0, -h);
        penUp();
    }

    /** Draw a box given an AWT Dimension for its size */
    public void drawBox(java.awt.Dimension d) {
        drawBox(d.width, d.height);
    }

    /** Draw a box given an AWT Rectangle for its location and size */
    public void drawBox(java.awt.Rectangle r) {
        moveTo(r.x, r.y);
        drawBox(r.width, r.height);
    }

    /** Show the current location; useful for
     * testing, if nothing else.
     */
    public Point getLocation() {
        return new Point(curx, cury);
    }
}

Note the variety of abstract methods. Those related to motion, pen control, or drawing are left abstract, due to the number of different ways of implementing motion on radically-different devices. However, the method for drawing a rectangle (drawBox) has a default implementation, which simply puts the currently selected pen onto the paper at the last-moved-to location, draws the four sides, and raises the pen. Subclasses for “smarter” plotters will likely override this method, but subclasses for less-evolved plotters will probably use the default version. This method also has two overloaded convenience methods for cases where the client has an AWT Dimension for the size or an AWT Rectangle for the location and size.

To demonstrate one of the subclasses of this program, consider the following simple “driver” program. This is intended to simulate a larger graphics application such as GnuPlot. The Class.forName() near the beginning of main is discussed in Recipe 17.2; for now, you can take my word that it simply creates an instance of the given subclass, which we store in a Plotter reference named “r” and use to draw the plot:

main/src/main/java/plotter/PlotDriver.java

public class PlotDriver {

    /** Construct a Plotter driver, and try it out. */
    public static void main(String[] argv) {
        Plotter r ;
        if (argv.length != 1) {
            System.err.println("Usage: PlotDriver driverclass");
            return;
        }
        try {
            Class<?> c = Class.forName(argv[0]);
            Object o = c.newInstance();
            if (!(o instanceof Plotter))
                throw new ClassNotFoundException("Not instanceof Plotter");
            r = (Plotter)o;
        } catch (ClassNotFoundException e) {
            System.err.println("Sorry, class " + argv[0] +
                    " not a plotter class");
            return;
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
        r.penDown();
        r.penColor(1);
        r.moveTo(200, 200);
        r.penColor(2);
        r.drawBox(123, 200);
        r.rmoveTo(10, 20);
        r.penColor(3);
        r.drawBox(123, 200);
        r.penUp();
        r.moveTo(300, 100);
        r.penDown();
        r.setFont("Helvetica", 14);
        r.drawString("Hello World");
        r.penColor(4);
        r.drawBox(10, 10);
    }
}

We’ll see example subclasses of this Plotter class in upcoming chapters.

1 A value class is one used mainly to hold state, rather than logic: a Person is a value class, whereas java.lang.Math is not. Many classes are somewhere in between.

2 Of course these lights wouldn’t have remote power-off. But the computers might, for maintenance purposes.

3 For Java folks not that familiar with C/C++, C is the older, non-OO language; C++ is an OO derivative of C; and Java is in part a portable, more strongly typesafe derivative of C++.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.96.86