Basic Usage of Objects - Language Usage - How to Use Objects: Code and Concepts (2016)

How to Use Objects: Code and Concepts (2016)

Part I: Language Usage

Chapter 1. Basic Usage of Objects

To learn a natural language properly, one goes abroad to live among native speakers for some time. One learns their idioms, their preferences in choosing words, and the general feeling for the flow of the language. But even so, when composing texts afterward, one turns to thesauri for alternative formulations and to usage dictionaries to acquire a desirable style.

This first part of the book will take you on a tour among Java natives, or at least their written culture in the form of the Eclipse IDE’s code base. We will study common idioms and usages of the language constructs, so as to learn from the experts in the field. At the same time, the categorization of usages gives us a vocabulary for talking about our daily programming tasks, about the purposes of objects, classes, methods, and fields, and about the alternatives we have encountered and the decisions we have made. In short, it helps teams to code more efficiently and to communicate more efficiently.

Like any usage dictionary, the presentation here assumes that you are in general familiar with the language; thus we will discuss the meaning of language constructs only very briefly. Furthermore, the chapter focuses on the technical aspects of usage. Advanced design considerations must necessarily build on technical experience and are discussed in Chapters 11 and 12. However, we give forward pointers to related content throughout, and encourage you to jump ahead if you find a topic particularly interesting. Finally, we discuss the Eclipse IDE’s tool support for the usages, because effective developers don’t write code, they have the code generated by Eclipse. Here, we encourage you to try the tools out immediately, just to get the feel for what Eclipse can do for you.

1.1 The Core: Objects as Small and Active Entities

Because programming languages are designed to offer relatively few, but powerful elements that can be combined in a flexible way, it is not the language, but rather the programmer’s attitude and mindset that determines the shape of the source code. As the well-known saying goes, “A realImage 210 programmer can write FORTRAN programs in any language.”

To get a head start in object-oriented programming, we will first formulate a few principles that set this approach apart from other programming paradigms. From a development perspective, these principles can also be read as goals: If your objects fit the scheme, you have got the design right. Because the principles apply in many later situations, we keep the discussion brief here and give forward references instead.


It’s the objects that matter, not the classes.


Learning a language, of course, requires mastering its grammar and meaning, so introductory textbooks on Java naturally focus on these subjects. Now it is, however, time to move on: The important point is to understand how objects behave at runtime, how they interact, and how they provideImage 1.4.12 Image 1.3.8 Image 1.4.8.4 services to each other. Classes are not as flexible as objects; they are merely development-time blueprints and a technical necessity for creating objects.Image 85 Learn to think in terms of objects!


Image Indeed, not all object-oriented languages have classes. Only in class-based languages, such as Java, C++, C#, and Smalltalk, is each object an instance of a class fixed at creation time. In contrast, in object-based languages, such as JavaScript/ECMAScript, objects are lightweight containers for methods and fields. Methods can even be changed for individual objects.


We start our overview of the characteristics of objects by considering how entire applications can be built from them in the end:


An application is a network of collaborating objects.


Image 11The idea of many small objects solving the application’s task together isImage 32,264,263 perhaps the central notion of object-oriented programming. While in procedural programming a few hundred modules are burdened with providing the functionality, in object-oriented applications a few hundred thousand objects can share and distribute the load. While classical systems featureImage 11.1 hierarchical module dependencies, objects form networks, usually with cycles: No technical restrictions must impede their collaboration on the task at hand.


Objects are lightweight, active, black-box entities.


Image 11.2When many objects solve a task together, each object can focus on a small aspect and can therefore remain small and understandable: It contains just the code and information relating to that aspect. To achieve aImage 1.8.2 Image 2.2 Image 1.8.6 clear code structure, it is helpful to assume that you can afford as many helper objects as you like. For instance, Eclipse’s SourceViewer, which is the basis for almost all editors, holds around 20 objects that contribute different aspects to the overall component (and around 50 more are inherited). Indeed, without that additional structure, theSourceViewer wouldImage 1.4.13 become quite unmanageable. Finally, objects are handled by reference—that is, passing objects around means copying pointers, which are mere machine words.

Objects are also active. While modules and data structures in classical software engineering primarily have things done to them by other modules,Image 72,205 objects are best perceived as doing things. For example, a Button does not simply paint a clickable area on the screen; it also shows visual feedback onImage 7.8 mouse movements and notifies registered objects when the user clicks theImage 7.1 Image 2.1 button.

Finally, objects are “black-box” items. Although they usually contain some extensive machinery necessary for performing their task, there is a conceptual box around the object that other objects do not penetrate. Fig. 1.1 gives the graphical intuition of how black-box objects should collaborate. Object B employs several helper objects, of which C implements some functionality that A requires. Since B is a black box, A should not make assumptions about its internal structure and cannot call on C directly.Image 2.2.3 Image 11.5.1 Image 12.1.4 Instead, A sends B a message m; that is, it calls its method m. Unknown to A, m now calls on C. Black-box objects do not publish their internal structure.

Image

Figure 1.1 Collaboration Between Self-Contained Objects


Image Being “black-box” means more than just declaring data structures and helpers as private or protected. Preventing others from accessing an object’s fields and internalImage 1.4.5 Image 1.4.8.2 methods at the language level is only the first step and really just a technical tool. This practice is called encapsulation, from the idea that the language enables you to establishImage 216(§7.4) an impenetrable capsule around the object. Beyond that, the concept of information hidingImage 11.5.1 Image 205 addresses creating “black-box” objects at the design level. What is hidden here isImage 216(§7.6) information about an object, which encompasses much more than just the definition of its technical internals. It may comprise its strategies for solving particular problems, its specific sequence of interactions with other objects, its choice in ordering the values in some list, and many more details. In general, information hiding is about hiding design decisions, with the intention of possibly revising these decisions in the future. In this book, Parts IIII deal mainly with encapsulation. Information hiding is discussed as a design concept in Part IV.


Creating black-box objects demands a special mental attitude, and skill, from developers:


Think of objects from the outside.


Developers adore the nifty code and data structures that they use to solve a problem. In a team, however, it is essential to learn to speak about an object from the perspective of the other team members, who merely wish to use the object quickly and effectively. Consider a combo box, of classCCombo on the screen. It enables the user to select one item from a given list in a nice pop-up window. Providing this simple functionality requires 1200 lines of code, using 12 fields with rather complex interdependencies.

Image 4 Image 6For a smooth development process, professionals must learn to describeImage 10 their objects’ behavior—their reactions to method invocations—in general terms, yet precisely enough for other objects to rely on the behavior. Their implementation is treated as a private, hidden internal, and isImage 11.2 encapsulated behind a public interface. You know that you have succeeded when you can describe your object in 1–2 brief sentences to your fellow team members.


Objects are team players, not lone wolves.


To emphasize the point of collaboration: Objects can focus on their own tasks only if they don’t hesitate to delegate related tasks that other objectsImage 1.4.1 Image 109 can perform better. Toward that goal, it also helps to imagine that objects communicate by sending messages to each other. A “method call” comes with many technical aspects, such as parameter passing and stack frames, that deflect the thoughts from the best design. It’s better to see this process as one object notifying another object, usually that it wants something done. Note also how the idea of objects working together requires lean public interfaces: Delegating tasks works well only if the other object states succinctly and precisely what it can do—that is, which tasks it can perform well.


Objects have an identity.


Objects are commonly used to represent specific things. Domain objects stand for things that the customers mention in their requirements; other objects may manage printers, displays, or robot arms. An object is therefore more than a place in memory to store data—its unique identity carries a meaning by itself, since the object is implicitly associated with thingsImage 1.8.4 Image 1.4.13 outside the software world. Except in the case of value objects, one cannot simply exchange one object for another, even if they happen to store the same data in their fields.

Note that this arrangement stands in contrast to classical data structures. Like objects, they reside in the program’s heap space. But, for instance, one hash map is interchangeable with another as long as both store the same key/value associations. The actual addresses of the hash map’s parts are irrelevant.


Objects have state.


Objects store data in their fields and—apart from a few special cases—thatImage 1.8.4 data changes over time. As we have just seen, objects frequently relate closely to the real world or our concepts about the world. The world is, however, stateful itself: When you write on a piece of paper, the paper is modified. When you type into an editor, you expect that the document is modified correspondingly. Unlike the real world, its software counterpart can support undo, by reversing modifications to the objects’ state. Furthermore,Image 9.5 the computer hardware is stateful by design, and objects at some point need to match that environment to work efficiently.


Objects have a defined life cycle.


Java makes it easy to work with objects: Just create them and keep a reference to them as long as they are required; afterwards, the garbageImage 133 collector reclaims them to reuse the memory.

To understand objects, it is often useful to consider the things that happen to an object during its existence more explicitly. The term life cycle captures this idea: An object is allocated, then initialized by the constructorImage 1.6.1 or by ordinary methods taking its role; then, its methods get called from theImage 1.6.2 outside to trigger certain desired reactions; and finally, the object becomesImage 1.4.1 obsolete and gets destroyed.

From the object’s point of view, these events are represented by calls to specific methods: It is notified about its creation and initialization, thenImage 12.3.3.4 the various operations, and finally its own upcoming destruction. These notifications serve to give the object the opportunity to react properly—for instance, by freeing allocated resources before it gets destroyed.


Don’t worry too much about efficiency.


Developers coming to Java often wonder whether it will be efficient enough. Unlike in C or C++, it is simply very difficult to estimate the actual runtime cost of their code. Their preoccupation with efficiency then sometimes leads them to trade object-oriented design for perceived improvements in efficiency. As Donald Knuth puts it, “Premature optimization is the root ofImage 140 Image 44(Item 55) all evil.”

Efficiency of code is, indeed, a dangerous goal: When it is stressedImage 258 too much, developers are likely to spend much effort on complex special-purpose data structures and algorithms. With the computing power now available on most devices, the trade-off between expensive developer time and cheap execution time is rapidly moving toward optimizing development and maintenance.


Image The trade-off might be obvious at the present time. But it is interesting that it has been valid from the infancy of modern computing. One of the seminal papersImage 236(p.125) on good software organization states, “The designer should realize the adverse effect on maintenance and debugging that may result from striving just for minimum execution time and/or memory. He should also remember that programmer cost is, or is rapidly becoming, the major cost of a programming system and that much of the maintenance will be in the future when the trend will be even more prominent.”


Code optimization therefore requires a strong justification, ideally by demonstrating the bottlenecks using a profiler. Without such a tool, a goodImage 115 guide is Amdahl’s law, which briefly says: “Make the common case fast.” The overall system performance improves only when we optimize code that runs frequently and that takes up a high portion of the system runtime anyway. Usually, this is the case in inner loops that are processing large amounts of data or performing nontrivial computations in each iteration. Symmetrically, it is not worth optimizing methods that run infrequently and usually work on very little data. As a case in point, consider the choice of linear data structures in ListenerList and AbstractListViewer in the Eclipse code base.


Image A profiler may actually be instrumental in finding the bottleneck at all. Because object-oriented code works with lots of small methods rather than long and deeply nested loops, the time may be spent in unexpected places like auxiliary hashCode() or equals() methods. The author once found that his program analysis tool written in C++ spent around 30% of its runtime in the string copy constructor invoked for passing an argument to a central, small method. Using a const& parameter eliminated the problem.


Furthermore, efficiency is not the same as program speed perceived by the user, and this speed can often be improved without using sophisticatedImage 50(§C.1) Image 174 data structures. For applications with user interfaces, it is usually sufficientImage 9.4.3 to reduce the screen space to be redrawn and to move more complex tasksImage 7.10 to background threads, or even to just switch to the “busy” mouse cursor.Image 8 Image 148 Multithreading can help to exploit the available CPU power. As these approaches suggest, optimization of perceived program speed is not so much about data structures and algorithms, but about good software organization. Moreover, this kind of optimization actually clarifies the structure, rather than making it more complex. The software will become more—not less—maintainable.

Finally, the concept of encapsulation ensures that you will not lose too much by starting with simple data structures and algorithms, as long as you keep their choice hidden inside objects. Once a profiler identifies a bottleneck, the necessary changes will usually be confined to single classes.

In summary, there is rarely a need for real optimization. You can design your code based on this assumption:


Objects are small and method calls are cheap.


You should not hesitate to introduce extra methods if they better documentImage 1.4.6 your overall approach and to introduce new objects (even temporary ones to be returned from methods) if they help to structure your solution.Image 133 Image 199


Image A particular concern of many C/C++ developers is the garbage collector. The HotSpot JVM’s collector offers many state-of-the-art collectors, among them a generational garbage collector. It acts on the assumption that “many objects die young”—thatImage 133 is, the program uses them only temporarily. The garbage collector keeps a small heap in which objects are created initially, and that heap is cleaned up frequently. Since the heap is small, this approach is very cheap. Only objects that survive a few collections are moved to larger heap areas that are cleaned less frequently, and with more effort.


1.2 Developing with Objects

Software development is more than just designing and typing code. It means working with and working on code that already exists or that is being written. Being a professional developer, then, is measured not only by the final outcome, but also by the process by which one arrives there.

1.2.1 Effective Code Editing in Eclipse

Programming is, or should be, a rather creative activity in the quest for solutions to given problems. Typing and formatting code, in contrast, is a mere chore, which easily distracts you from the solution. The goal is this:


Don’t type your Java code—let Eclipse generate it for you.


While going through the language constructs in detail, we will point out the related Eclipse tool support. Here, we give a first, brief overview. To avoid lengthy and redundant enumerations along the menu structure, we give a motivational choice and encourage you to try the tools whenever you code.

1.2.1.1 Continuous Code Improvements

Two tools are so useful that developers usually invoke them intermittently, without special provocation: code formatting and organization of imports.


Tool: Format Code

Press Ctrl-Shift-F (for Source/Format) in the Java editor to format the current source file according to the defined code conventions.


Image 241Code conventions define rules for formatting—in particular, for line breaks and indentation—that make it simpler for developers to share source code: If all source of a project is laid out consistently, developers get used to the style and are not distracted by irrelevant detail. With Eclipse, obeying code conventions is simple and there is no excuse for ill-formatted code. In the Preferences/Java/Code Style/Formatter, you can even fine-tune the conventions used to fit your requirements.


Image You can also change these settings from a project’s Properties dialog, which writes them to the .settings folder within the project. As a result, they will be checked into version control systems with the code and will be shared in the team. Alternatively, you can export and import the workspace-wide formatting settings.



Image Formatting does not work for source code with syntax errors. If Ctrl-Shift-F does not react, fix any remaining errors first.


Java requires import declarations to access classes or static methods from other packages. Of course, these are not meant to be written by hand:


Tool: Organize Imports

Press Ctrl-Shift-O (Source/Organize Imports) to remove unused imports and add imports for unresolved names. If there are ambiguities, Eclipse will show a selection dialog to resolve them.



Image Since the compiler by default issues warnings about unused imports, Eclipse can even invoke the tool whenever a file is saved (see Preferences/Java/Editor/Save Actions).


1.2.1.2 Navigation

In real-world projects, it is necessary to keep an overview of large code bases. When learning APIs and new frameworks, you also need to see related code quickly. It is worthwhile to get used to the keyboard shortcuts.

The Navigation menu offers a huge selection of available tools. Here are some appetizers: F3 jumps to the declaration of the name under the cursor; pressing Shift and hovering with the mouse over an identifier shows the definition in a pop-up (enable in Preferences/Java/Editor/Hovers);F2 shows the JavaDoc. With Ctrl-Shift-T you can quickly select a class, interface, or enum to jump to; Ctrl-Shift-R jumps to general resources.

There are also many special-purpose views, which are placed beside the editor: F4 shows the position of a class in the type hierarchy; that view’s context menu then lets you move through the hierarchy by focusing on different classes. The outline reflects the structure of the current class,and a double-click jumps to the element; you can even rearrange elements by drag-and-drop. With Ctrl-Alt-H, you can navigate through the call hierarchy view to understand the collaboration between methods across classes. A second access path to such views is found in the Show in ...menu, which you reach in the Java editor by Alt-Shift-W. This menu will save you a lot of manual tree navigation in the package explorer.

To move within a class, invoke the quick outline with Ctrl-O, then type the beginning of the target method’s or field’s name. To also see declarations in the super-types, press Ctrl-O again.

1.2.1.3 Quick-Fix

Quick-Fix (Ctrl-1) was historically intended to fix simple errors. More recently, it has developed into a standard access path to powerful tools for code generation and modification. Very often, it is simpler to deliberately write wrong or incomplete code and then use Quick-Fix to create the intended version. We can give here only a few examples, and encourage you to invoke the tool frequently to build a mental model of what it can do for you.

First, Quick-Fix still fixes simple errors. It adds required imports, changes typos in names, and rearranges arguments of method calls to resolve type errors. When you call a nonexistent method, it creates the method for you. When you write an abstract method, it proposes to make the class abstract for you; when the method has a body, Quick-Fix can remove it. When your class implements an interface, but does not have the methods, Quick-Fix adds them. When you call a method that expects an interface, it offers to add an implements clause to the argument object or to add a cast. When you assign to a local variable of the wrong type, or call a method with a wrong parameter, it can change the target type, or the source type, to achieve a match.

The real power of these fixes comes from using combinations. For instance, if you want this to receive notifications about changes in a textImage 7.1 field on the screen, just type txt.addModifyListener(this). Quick-Fix first adds the required implements clause, then creates the required method declarations for you.

Quick-Fix is also good at generating and modifying code. Sometimes, while the code may compile, it may not be what you had in mind. When you have written an expression, Quick-Fix can place the result in a new variable declaration. It will even extract the subexpression under the cursor to a new variable. When you declare a variable and initialize it on the next line, Quick-Fix can join the variable declaration when the cursor is in the variable name on either line. In if and while statements, Quick-Fix can add and remove curly braces in single-statementthen/else blocks and the loop body, respectively.


Image Linked positions are shown as boxes in generated code when the generation involves choices or ambiguities. Using tab, you can navigate between the linked positions and then choose the desired version from the appearing pop-up menus.


1.2.1.4 Auto-Completion

Auto-Complete (Ctrl-Space) in many editors means finding extensions to the name under the cursor. In Eclipse, it means guessing what you were probably about to write. As with Quick-Fix, it is useful to invoke the tool very often to learn about its possibilities.

In its basic capacity, Auto-Complete will propose extensions to type, method, and field names. It will also add import declarations as necessary. Using CamelCase notation often simplifies the input. To get, for instance, IFileEditorInput, just auto-complete IFEI; since there is only one completion, it expands immediately. When looking for method names, Auto-Complete uses the type of the invocation target. But it does even more: If the current position is guarded by an instanceof test, it offers the methods of the specialized type and adds the required cast.


Image Under Preferences/Java/Editor/Content Assist, you can include or exclude names that are not actually available at the current point. In exploratory programming, at the beginning of projects, or with new libraries, it is often useful to get all proposals, even if they result in a compilation error; you can always quick-fix that error later on.



Image Image 12.3.3 Image A.1.2When working with plugins, it is often useful to auto-complete even types from plugins that are not not yet referenced by the current project. To enable this, open the Plug-ins view, select all entries, and choose Add to Java Search from the context menu. You can later use Quick-Fix to add the missing dependencies.


Auto-Complete also includes many code templates. Expanding the class name, for example, yields a default constructor. At the class level, a method name from the superclass creates an overriding method; completing get or set offers getters and setters for fields; static_finalcompletes to a constant definition. Expanding toarray calls the toArray() method of a collection in the context; you can choose which one through linked positions.

1.2.1.5 Surround With

Developing code is often an explorative process: You write down part of a larger computation and only later realize that it should actually be guarded by an if, or must run in a different thread altogether, so that it must beImage 148 packaged into a Runnable object. The tool Surround With(Alt-Shift-Z) offers a choice of handy modifications that often need to be applied as an afterthought in daily work.

1.2.1.6 The Source Menu

An obvious place to look for code generation patterns is the Source menu. We have saved it for last because many of its tools are also available more easily through Auto-Complete or Quick-Fix. Yet, this menu often offers more comprehensive support. For instance, you can generate getters and setters for several fields, or override several methods at once. In practice, you will soon get a feel for whether the extra effort in going through the menu and a dialog offers any advantages over invoking the tool through other access paths. It is also worthwhile to get used to keyboard shortcuts to the menu items. For instance, Alt-S R is handy for generating getters and setters for fields; Alt-Shift-S shows a pop-up version of the menu over the editor.

1.2.2 Refactoring: Incremental Design Improvements

In the early days of computing, it was commonly thought that a softwareImage 54 project should progress in a linear fashion: Gather the requirements from the users, lay down the system architecture, then the design, then specify the single classes and their methods, and finally implement and test them. This was called the waterfall model. Unfortunately, the waterfall has washed away many a software project.

Later software processes acknowledge that one learns during development by including cycles that allow going back to earlier project phases.Image 233 Agile software development then established truly iterative development,Image 28 and demanded a focus on the code, rather than on plans and documents. The challenge is, of course, that the design will change when the code already exists.

At a smaller scale, every developer knows that coding an object yields new insights on how the object should best be designed. After having written out the solution, one simply understands more of the solution’s structure.


Expect to adapt objects to new design insights.


When you find that a new base class is a good place for shared functionality,Image 3.1.3 introduce it. When you find that your colleague’s tangled method canImage 1.4.5 Image 1.4.6 be perceived as a few high-level processing steps, introduce them. When you think of a better name for some variable, change it. A slogan in theImage 1.2.3 community nowadays is “Leave the campground cleaner than you found it.”Image 172

Of course, it won’t do to anarchically change the design every few days. There must be some discipline to avoid breaking other classes and delaying the project’s progress.Image 92


Refactoring means improving the design without changing functionality.


Refactoring applies to existing, working, running, productive code. To avoidImage 5.4.8 accidental breakage, the overall code base should be well tested. However, you can also write tests just to capture the current functionality of a specific object, and then go ahead and refactor it.


Refactoring is a transaction that takes a running application to a running application.


Writing tests for “obvious” modifications such as changing names seems, of course, so cumbersome that no one would do it. More generally, many frequent refactorings are syntactic in nature, and there is little danger of accidents. Eclipse provides a broad and stable tool support for refactorings, which we will introduce throughout this chapter, together with the constructs that they apply to.


Learn to use the Eclipse refactoring tools.


The Eclipse tools for reliable refactorings are accessible through a common menu, very often through the context menu:


Tool: Eclipse Refactoring Tools

In most circumstances, select the element to be modified and press Alt-Shift-T to invoke the refactoring context menu.


One word of warning is in order: Cleaning up the structure often yields opportunities to add new functionality “while you’re looking at the class anyway.” Indeed, refactorings are often applied precisely because new functionality will not fit the existing structure. However, you should not yield to temptation. First, apply the planned sequence of refactorings and restore the old system behavior. Then, commit the changes to your versioning system. Only at that point should you change the functionality.


Don’t introduce new functionality during refactoring.


This rule is often used to argue that refactoring is a wasted effort: The point is not to change the functionality, but then functionality is what the customer pays for. This argument is short-sighted, because it neglects the internal cost of implementing the requested functionality:


Refactoring makes developers more productive.


Refactoring is usually essential to achieve a project’s goals with less effort and sometimes to achieve them at all. Refactoring changes the software structure so that new functionality will fit in more easily. It can separate special logic from general mechanisms and can enable reuse of the general parts. It makes the code more readable and more understandable. As a result, it reduces the time spent on debugging and on digging into the code written by other team members. During maintenance—and maintenance is the most cost-intensive part of the software life cycle—developers will find their way around the code more easily and will make the necessary adaptations with more confidence and in less time. In the end, refactoring is not a matter of taste in software design, but rather translates into direct gains in the cost of software production.

1.2.3 The Crucial Role of Naming

Whenever we code, we choose names: for variables, fields, methods, classes, packages, and so on. These names are for the human readers: for your fellow team members, for the later maintenance developers, and for yourself if you happen to come back to the code a few months later. Carefully chosen names can convey meaning and intention, while poorly chosen names may mislead and confuse readers and make them spend more time trying to decipher the code than necessary. The literature contains many guidelines and hintsImage 55 Image 172(Ch.17, N) on naming. Here, we give a general overview to encourage you to considerImage 263(p.67, p.69, p.88ff) naming a central activity in software development.


Think of names as documentation.


Most developers dislike documentation, because it takes away time from coding, gets outdated quickly, and is not read anyway. Not writing documentation means, however, that others will have to understand the code. Luckily, there is a simple way out: All language elements, from classes to local variables, have names that you can use to express your intention inImage 11.2.1 writing the code. Knowing your intention will help future readers to grasp the working of the code. This gain in productivity motivates a simple guideline:


Invest time in finding the most appropriate names.


Suppose you are writing a data processing tool that deals with table-like structures, similar to relational database systems. You will have objects representing single data records. Without much thought, you could call these “data items”—but then, that’s not very specific, since “item” has a rather fuzzy meaning. When focusing on the table structure, you might prefer “data row” or simply “row.” In the context of databases, however, you might speak of a “record.” Try out different variants, drawing on established names and your experience. You may also employ a thesaurus for inspirationImage 141 about closely related words.

As with any choice, you may find that the name that was best at one point later turns out to be unsuitable. For instance, when writing a loop that traverses a string, you may have introduced an index pos for the current position. As you proceed, you discover several further “positions”: the first occurrence of some character, the end of some substring, and so on. To make the code more readable, you should change pos into curPos or even searchPosition, to describe the content more precisely. Fortunately:


There is no excuse for keeping bad names.


Changing names is so common that Eclipse provides extensive support for this operation. For novices, it may be daunting to go through theImage 1.2.2 Refactoring menu, but that place was chosen merely to emphasize that renaming is a proper structural code modification that does not alter the meaning of the code.


Tool: Renaming

Place the cursor over any name in the editor, or select an element in the package explorer. Then press Alt-Shift-R or use the Refactoring/Rename menu (Alt-Shift-T).


One important exception to changing bad names immediately is, of course, in the public interface of your software: If you offer functionality to others, your clients’ code will be broken. As an extreme example, there is the caseImage 61 of the function SHStripMneumonic in the Windows API—once it was published, there was simply no way to correct the name to SHStripMnemonic.

A general guideline for choosing good names derives from the fact that humans tend to jump to conclusions:


Use similar names for similar things, and different names for different things.


When humans see a ScreenManager and a Display in the system, they will assume that someone was sloppy and the former actually manages the latter. If this is the case, rename ScreenManager to DisplayManager; otherwise, choose a completely different name, such asWindowLayoutManager (if that is its task). To make the point very clear, let’s look at an exampleImage 214 where the rule has been disobeyed. The developer guide of Eclipse’s Graphical Editing Framework (GEF) states somewhat awkwardly:

The “source” and “target” nodes should not be confused with “source” and “target” feedback. For feedback, “source” simply means show the feedback for the connection, while “target” means highlight the mouse target.

Since names serve communication purposes, they often crop up in discussions among the team. For this situation, it is important to obey a simple rule:


Make names pronouncable.


This strategy also implies that abbreviations should in general be avoided, unless they have an obvious expansion, which can then be pronounced. Note that auto-completion invalidates the excuse that abbreviations reduce theImage 1.2.1.4 typing effort. In fact, use of CamelCase often makes it easier to enter the longer, pronouncable name.

The goal of communication also implies that names should conjure up associations in the reader’s mind.


Use names to refer to well-known concepts.


For instance, if a name includes the term “cache,” then the reader will immediately be aware that it contains temporary data that is kept for efficiency reasons, but is really derived from some other data.

If a concept is very general, you should qualify it further through composite names. For instance, the associations of a “hash map” are clear. The more specific class IdentityHashMap then turns out to associate values to objects based on object identity, instead of its equals andhashCode methods. However, the reference to well-known concepts is not unproblematic,Image 1.4.13 since it depends on the intended group of readers. Therefore:


Choose names to fit the context.


Names are often linked to the project, team, and and part of the system. At a basic level, coding conventions may dictate, for example, that fields are prefixed by f. Look at, for instance, JavaTextTools and other classes from the Eclipse Java tooling for examples. Default implementations of interfacesImage 3.1.4 are often suffixed with Adapter, such as in SWT’s MouseAdapter. Further, patterns come with naming conventions. For example, observer interfacesImage 2.1 in Java usually have the suffix Listener. Similarly, in the Eclipse platform the update method from the pattern is called refresh. Examples are seen in JFace’s Viewer class and the EditPart from the GraphicalImage 9.3.1 Image 214 Editing Framework. Finally, the layer of the object is important: Domain objects have domain names, such as BankAccount, while technical objects have technical names, such as LabelProvider.

Sometimes, it can help to merge several views:


Choose compound names to indicate different aspects.


For instance, a BankAccountLabelProvider clearly is a technical object that implements a LabelProvider for domain-level BankAccounts.

One distinction to be obeyed painstakingly is that between the externalImage 4.1 Image 1.1 and internal views of an object: The public methods’ names must not refer to internal implementation decisions.


Public names must be understandable without knowing the internals.


You can see whether you have got the naming right if you have achieved a simple overall goal:


Choose names such that the source code tells its own story.


Code telling a story is easy to recognize. Suppose you read through a longish piece of code that calls some methods, accesses a few fields, and stores temporary results in local variables. At the same time, you have a good sense of what is going on, because the names establish conceptual linksImage 141 between the various steps and data items: This is code that tells a story. Make it a habit to look through code that you have just finished and toImage 1.4.5 rearrange and rename until you are satisfied with the story.

Developers are a close-knit community, and one that is partly held together by common jokes, puns, and folklore. Nevertheless, we hope that you are by now convinced that names are too important to sacrifice them to short-lived merriment:


Don’t joke with names.


Here is an example.1 At some point, someone found it funny to use a Hebrew token name for the namespace separator :: in PHP. Unfortunately, this “internal” choice later turned up in error messages to the user, confusing everyone not in the know:

parse error, unexpected T_PAAMAYIM_NEKUDOTAYIM

1. http://php.net/manual/en/keyword.paamayim-nekudotayim.php

Such occurrences are so common that there are collections of rules to avoid them.2 Names referring to the author’s favorite movie, pseudo-random words such as starship, and “temporary” names with my and foo in them are known to have made it into releases.

2. See, for example, http://thc.org/root/phun/unmaintain.html and http://c2.com/cgi/wiki?BadVariableNames

1.3 Fields

An object’s fields are usually at the core of operations: They store the information that the object works on, the knowledge from which it computesImage 1.1 Image 4.1 answers to method calls, and the basis on which it makes decisions. From the larger perspective of the overall system, however, this core of an object is a private, hidden detail. Consequently, other objects must not make any assumptions about which fields exist and what they contain.


An object’s fields are its private property.


The seminal object-oriented language Smalltalk takes this goal very seriously:Image 109 Only the object itself can access its fields (including those inherited from its superclass); field access across objects is impossible. In Java, accessImage 232 rights follow the philosophy of “participating in the implementation”: An objectImage 111 can access private fields of other instances of its own class, protected fields can be accessed from all subclasses and classes in the same package, and default visible fields (without modifiers) are shared within the package. public fields are even open to the world in general.

While all fields, technically speaking, store data, general usage differentiates between various intentions and interpretations associated with that data. Anticipating these intentions often helps in understanding the fields of a concrete object and their implied interdependencies. Before we start, one general remark should be noted:


An object’s fields last for its lifetime.


Fields are initialized when the object is created by the constructor. Afterward,Image 1.6.1 they retain their meaning until the object is picked up by the garbage collector. At each point in time, you should be able to say what each fieldImage 4.1 contains and how it relates to the other fields. In consequence, you should refrain from “reusing” fields for different kinds of data, even if the type fits. It is far better to invest in a second field. Also, you should avoid having fields that are valid only temporarily, and prefer to introduce helper objects.Image 1.8.6

1.3.1 Data Structures

At the most basic level, objects use fields to maintain and structure their data. For instance, the GapTextStore lies at the heart of text management in the Eclipse source editors. It maintains a possibly large text efficiently in a flat array and still provides (mostly) constant time manipulations for frequent operations, such as typing a single character.

Figure 1.2 depicts the meaning of the following fields:

org.eclipse.jface.text.GapTextStore


private char[] fContent;
private int fGapStart;
private int fGapEnd;


Image

Figure 1.2 The GapTextStore Data Structure

The fContent is the flat storage area. The gap between fGapStart and fGapEnd is unused; the remainder stores the actual text in two chunks. Text modifications are performed easily at fGapStart: New characters go into the gap and deletions move the gap start backward. To modify other positions, the object moves the gap within the buffer, by copying around the (usually few) characters between the gap and the new start. The array is resized only in the rare event that the gap becomes empty or too large.

Image 72 This is a typical data structure, like the ones often found in textbooks. In such a structure, primitive data types are combined to represent some abstract value with operations, here a text with the usual modifications. It is also typical in that the object’s interface is very simple and hidesImage 4.1 the intricate case distinctions about moving and resizing the gap—that is, clients merely invoke the following method to remove length characters at offset and insert the string text instead.

org.eclipse.jface.text.GapTextStore


public void replace(int offset, int length, String text)


Data structures can also be built from objects, rather than primitive types.Image 72 For instance, the JDK’s HashMap uses singly linked lists of Entry objects to represent buckets for collision resolution. As in the case of primitive types, the HashMap contains all the logic for maintaining the data structure in the following fields. Entry objects have only basic getter-like methods and serve as passive containers of information, rather than as active objects.

java.util.HashMap


transient Entry[] table;
transient int size;
int threshold;
final float loadFactor;



Image The transient modifier states that the field is not serialized to disk in the default manner. Instead, Java’s serialization mechanism invokes writeObject and read Object from HashMap.

The final modifier states that the field must not be altered after it has been initialized in the constructor. The compiler also tracks whether the field is, in fact, initialized.


Data structures are frequently constructed from larger and more powerful building blocks, in particular from the collections framework. For instance,Image 9.3.2 the JFace AbstractListViewer displays lists of data items. It maintains these items in a general list, rather than an array, because that facilitates operations:

org.eclipse.jface.viewers.AbstractListViewer


private java.util.List listMap = new ArrayList();


The common theme of these examples is that the main object contains all the logic and code necessary to maintain the data structure fields. Even if those fields technically do contain objects, they are only passive information holders and do not contribute any functionality on their own—they perform menial housekeeping tasks, at best.


Don’t implement tasks partially in data structure objects.


Mentally classifying fields as “data structures” helps to clearly separate concerns, and you know that the contained objects are uninteresting when it comes to maintenance and debugging. At the same time, the work is clearly divided—or rather not divided in that the host object takes it on completely. If you do want helper objects to contribute, do so properly andImage 1.3.2 Image 1.8.2 Image 1.8.5 give them self-contained tasks of their own.

1.3.2 Collaborators

Objects are team players: When some other object already has the data andImage 1.1 logic for performing some task, they are happy to delegate that task. One can also say that the objects collaborate. Very often, an object stores itsImage 11.1 collaborators in its fields, because it refers to them frequently throughout its lifetime. In contrast to data structures, an object entrusts collaborators with some part of its own specific responsibilities.

The Eclipse platform’s JobManager provides a good example. Its purpose is to schedule and track all background Jobs, such as compiling JavaImage 7.10 files. This task is rather complex, since it has to account for priorities and dependencies between jobs. The manager therefore delegates some decisions to JobQueue objects held in three fields, for different groups of jobs. The method JobQueue.enqeue(), with its helpers, then takes care of priorities and resource dependencies.

org.eclipse.core.internal.jobs.JobManager


private final JobQueue sleeping;
private final JobQueue waiting;
final JobQueue waitingThreadJobs;


In contrast, the management of the currently running jobs is a core task of the JobManager itself, and the necessary logic belongs to that class. The bookkeeping is therefore performed in mere data structures, rather thanImage 1.3.1 self-contained objects. The JobManager is responsible for keeping up the expected relationships between the two sets—we will later see that theseImage 4.1 relationships become part of its class invariant.

org.eclipse.core.internal.jobs.JobManager


private final HashSet running;
private final HashSet yielding;


Image 2.2All of these examples incorporate the notion of ownership: The JobManager holds the sole references to the collaborators, the manager creates them,Image 1.1 and their life cycle ends with that of the manager.

Collaboration is, however, not restricted to that setting; indeed, trueImage 1.1 networks of collaborating objects can be built only by sharing collaborators.Image 12.3.3.4 As an extreme example, the Eclipse IDE’s UI is composed from different editors and views, both of which are special workbench parts. Each such part holds a reference to the context, called a site, where it appears:

org.eclipse.ui.part.WorkbenchPart


private IWorkbenchPartSite partSite;


Through that site, views and editors can change the title on their tabs, and even access the overall workbench infrastructure, to observe changes, open and close parts, and perform other tasks.

1.3.3 Properties

Image 1.1In general, objects treat their fields as a private matter that is no one else’s concern. In this manner, they are free to change the internal data format if it turns out that the current choice is inadequate. However, sometimesImage 1.8.3 the task of some object is precisely to hold on to some information, and its clients can and must know about it. Such fields are called properties, and the object offers getters and setters for their properties—that is, methods named get Imageproperty nameImage and set Imageproperty nameImage, respectively. For Boolean properties, the getter is named is Imageproperty nameImage. These methods are also collectively called accessors.

Image 9.3.4For instance, a JFace Action encapsulates a piece of functionality that can be put into menus, toolbars, and other UI components. It naturally has a text, icon, tool tip text, and other elements, so these fields are directly accessible by setters and getters. For more examples, just search for method declarations named set* inside Eclipse.


Tool: Generating Getters and Setters

Since properties are so common, Eclipse offers extensive tool support for their specification. The obvious choice is Source/Generate Getters and Setters (Alt-S R or Alt-Shift-S R). You can also auto-complete get and set in the class body, possibly with a prefix of the property name. When the cursor is on the field name, you can choose Encapsulate Field from the refactoring menu (Alt-Shift-T), or just invoke Quick-Fix (Ctrl-1). The latter two tools will also make the field private if it was public before.



Image Don’t generate getters and setters lightly, simply because Eclipse supports it. Always remember that an object’s data is conceptually private. Only fields that happen to fit the object’s public description are properties and should have accessor methods.



Image Beware that the generated getters return objects stored in fields by reference, so that clients can modify these internal data structures by calling the objects’ methods. This slip happens often with basic structures such as ArrayLists or HashMaps, and Eclipse does not recognize it. You must either return copies or wrap the objects by Collections.unmodifiableList() or similar methods. Similarly, when clients pass objects to setters, they may have retained a reference, with the same problematic results.


Sometimes, the stored information is so obvious and elementary that the fields themselves can be public. For instance, SWT decides that aImage 7.1 Rectangle obviously has a position and a size, so making the fields x, y, width, and height public is hardly giving away any secrets. Besides, the simplicity of the class and the data makes it improbable that it will ever be changed.

Even more rarely, efficiency requirements may dictate public fields. For instance, Positions represents points in a text document. Of course, these must be updated upon each and every text modification, even when only a single character is typed. To enableDefaultPositionUpdater to perform these frequent updates quickly, the position’s fields are public (following Amdahl’s law).Image 1.1

It is also worth noting that sometimes properties are not backed by physical fields within the object itself. For instance, the accessors of SWT widgets often delegate to a native implementation object that actually appearsImage 7.1 on the screen. In turn, a Label’s foreground color, a Textfield’s content, and many more properties are stored only at the native C layer. Conceptually, this does not change their status as properties, and tools such as the WindowBuilder do rely on the established naming conventions.Image 7.2

Finally, the JavaBeans specification defines further support. When bound propertiesImage 202 are changed, beans will send notifications to PropertyChange Listeners according to the OBSERVER pattern. For constrained properties,Image 2.1 observers can even forbid invalid changes.

1.3.4 Flags and Configuration

Properties usually contain the data that an object works with, or that characterize its state. Sometimes, they do more: The value of the property influences the object’s behavior and in particular the decisions that theImage 1.1 object makes. Knowing that the property has more influence than mere passive data is essential for understanding and using it correctly.

As a typical example, consider an URLConnection for accessing a web server, usually over HTTP. Before it is opened, the connection can be configured to enable sending data, by timeout intervals, and in many other ways. All of these choices are not passed on as data, but influence the connection’s behavior.

java.net.URLConnection


protected boolean doOutput = false;
private int connectTimeout;
private int readTimeout;


Boolean configuration properties are called flags. Very often, they are stored in bit masks to save space. In the following snippet from SWT’s text field, the READ_ONLY bit is first cleared, then perhaps reset if necessary. The style bit field here is shared through the built-in Widgethierarchy.

org.eclipse.swt.widgets.Text


public void setEditable(boolean editable) {
style &= ~SWT.READ_ONLY;
if (!editable)
style |= SWT.READ_ONLY;
}


Beyond elementary types, configuration properties may also contain objects. An object is given a special collaborator, with the intention of defining or modifying its behavior by specifying the desired collaboration. For instance,Image 7.5 all Composite widgets on the screen must somehow arrange the contained child elements. However, there are huge differences: While toolbars create visual rows of their children, forms often place them in a tabular arrangement. A composite’s behavior can therefore be configured by a Layout, which computes the children’s positions on behalf of the composite widget. The predefined choices such as RowLayout, GridLayout, and StackLayout cover the most common scenarios.

Configuration by objects in this way is an application of the STRATEGYImage 12.3 Image 100 pattern:


Pattern: Strategy

Encapsulate algorithms (i.e., solutions to a given problem) with a common interface so that clients can use them interchangeably.

1. Identify the common aspects of the various solutions and define an interface (or abstract base class) Strategy capturing the access paths and expected behavior.

2. Define ConcreteStrategy objects that implement Strategy.

3. Optional: Rethink your definitions and refactor to enable clients to provide their own concrete strategies.


After you have performed these steps, objects can be parameterized by a strategy by simply storing that strategy in a property.

A second use of the STRATEGY pattern is to encapsulate algorithmsImage 1.8.6 as objects, without the goal of abstracting over families of algorithms. In this case, the complexities of the algorithm can be hidden behind a small, readable interface. If the family of algorithms is not to be extensible, theImage 3.1.6 pattern might degenerate to a reified case distinction.

1.3.5 Abstract State

An object’s state consists, in principle, of the current data stored in its fields. Very often, it is useful to abstract over the individual fields and theirImage 10 data structures, and to assign a small number of named states instead. For example, a button on the screen is “idle,” “pressed,” or “armed” (meaning releasing the button now will trigger its action); a combo box has a selection list that is either opened or closed. These summary descriptions of the object’s state enable clients to understand the object’s behavior in general terms. For instance, the documentation may state, “The button sends a released notification if it is in the armed state and the user releases the mouse button.”

Sometimes, the abstract state is reflected in the object’s fields. WhileImage 10.3 direct enumerations are rare, combinations of Boolean flags that determine the state are quite common. For instance, a ButtonModel in the GraphicalImage 214 Editing Framework has a bit field state for that purpose (shown slightlyImage 1.3.4 simplified here):

org.eclipse.draw2d.ButtonModel


protected static final int ARMED_FLAG = 1;
protected static final int PRESSED_FLAG = 2;
protected static final int ENABLED_FLAG = 16;
private int state = ENABLED_FLAG;


A second, very instructive example is found in Socket, whose state fields reflect the sophisticated state model of TCP network connections.Image 237

While it is not mandatory to make the abstract state explicit in the concrete state, it often leads to code that tells its own story. For instance,Image 1.2.3 the setPressed() method of ButtonModel is called whenever the user presses or releases the mouse button. The previously given documentation is then directly expressed in the method’s control flow, especially in lines 5–6 (value is the new pressed/non-pressed state).

org.eclipse.draw2d.ButtonModel.setPressed


1 setFlag(PRESSED_FLAG, value);
2 if (value)
3 firePressed();
4 else {
5 if (isArmed())
6 fireReleased();
7 else
8 fireCanceled();
9 }


1.3.6 Caches

Designing networks of objects sometimes involves a dilemma between a natural structure that reflects the specification and problem domain directly,Image 1.1 and the necessity to make frequently called methods really fast. This dilemma is best resolved by choosing the natural structure and method implementations to ensure correctness, and by making the methods store previously computed results in index data structures, such as HashMaps.


Caches hold derived data that could, in principle, be recomputed at any time.


A typical example is found in the Eclipse JDT’s Java compiler. The compiler must track super-type relationships between defined classes and interfaces, and the natural structure is simple: Just store the types from the source code directly (a ReferenceBinding is an object representing a resolved type, either from the source or from a library):

org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding


public ReferenceBinding superclass;
public ReferenceBinding[] superInterfaces;


For any assignment c=d, the compiler will determine the types C and D of c and d, respectively. It must then decide whether a D object is valid for a C variable. This question clearly shows the dilemma mentioned previously: It is central for the correctness of the compiler, and should be implemented along the language specification by just searching through the superclass and superInterfaces. At the same time, it must be answered very oftenImage 1.1 and very fast.

Caching comes to the rescue: A ReferenceBinding implements the specification directly in a private method isCompatibleWith0. The method implements a linear search through the super-types and is potentially expensive. The public method isCompatibleWith therefore wraps calls by consulting a cache. The following code is slightly simplified. Line 1 looks up the previous result, which can be either true, false, or null. If the result is known (i.e., result is not null) then that result is returned (lines 2–3). Otherwise, the linear method is called. However, there is a snag: The search could end up in an infinite recursion if the type hierarchy contains a cycle. This is resolved by placing false into the cache and letting each recursion step go through the public method—a cycle ends in returning false immediately in line 3; at the same time, the recursion itself can take advantage of the cache. If despite this check the call in line 7 returns true, then no cycle can be present, so the result is updated to true (line 8). With this setup, the answer to the subtyping query is computed only once for any pair of types.

org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding.isCompatialbleWith


1 result = this.compatibleCache.get(otherType);
2 if (result != null) {
3 return result == Boolean.TRUE;
4 }
5 // break possible cycles
6 this.compatibleCache.put(otherType, Boolean.FALSE);
7 if (isCompatibleWith0(otherType, captureScope)) {
8 this.compatibleCache.put(otherType, Boolean.TRUE);
9 return true;
10 }


Caches are also a prime example of encapsulation, which is often motivatedImage 1.1 Image 4.1 Image 11.5.1 precisely by the fact that the object is free to exchange its internal data structures for more efficient versions. Callers of isCompatibleWith are not aware of the cache, apart from perhaps noting the superb performance. The presence or absence of the cache does not change the object’s observable behavior.


Image One liability of caches is that they must be kept up-to-date. They contain informationImage 6.1.5 derived from possibly large object structures, and any change of these structures must be immediately reflected in the cache. The OBSERVER pattern offers a general approachImage 2.1 to this consistency problem. However, you should be aware of the trade-off between the complexity of such a synchronization mechanism and the gain in efficiency obtained by caching. The question of optimizations cannot be evaded by caches.Image 1.1



Image Caches are effective only if the same answer is demanded several times. In other contexts, this behavior is also called locality of reference. In the case of a cache miss, the ordinary computation needs to be carried out anyway, while the overhead of maintaining the cache is added on top. In the case of the compiler, it is probable that a medium-sized code base, for instance during the recompilation of a project or package, will perform the same type conversion several times.



Image For long-lived caches, you must start to worry about the cache outgrowing the original data structure, simply because more and more answers keep accumulating. The central insight is that cache entries can be recomputed at any time, so you are free to discard some if the cache gets too large. This strategy is implemented by the JDT’sImage 99 JavaModelCache, which uses several ElementCaches to associate heavyweight information objects with lightweight IJavaElement handles. Sometimes, a more elegant solution isImage 50 to leverage the garbage collector’s support for weak references: A WeakHashMap keeps anImage 133 entry only as long as its key is referenced from somewhere else.


1.3.7 Data Shared Between Methods

A central goal in coding methods is to keep the individual methods shortImage 1.4.5 and readable, by introducing separate methods for contained processing steps. Unfortunately, passing the required data can lead to long parameter lists, which likewise should be avoided. The obvious solution is to keep the data required by several methods in the object’s fields.

Image 1.3.4 Image 1.8.6 This approach applies in particular when complex algorithms are represented by objects according to the STRATEGY pattern. Examples can be found in the FastJavaPartitionScanner, which maintains the basic structure of Java source code within editors, or in Zest’s graph layout algorithms, such as SpringLayoutAlgorithm, but also in simple utilities such as the JDK’s StringTokenizer.

When coding methods and their submethods, you often find out too late that you have forgotten to pass a local variable as a parameter. When you expect that the data will be needed in several methods anyway, Eclipse makes it simple to keep it in a field directly:


Tool: Convert local Variable to Field

Go to the local variable declaration in the Java editor. From the Refactoring menu (Alt-Shift-T), select Convert local variable to field. Alternatively, you can use Quick-Fix (Ctrl-1) on the variable declaration.


This tool is particularly useful when the local variable contains an intermediateImage 7.1 node in a larger data structure. For instance, when building a widget tree for display, you cannot always anticipate all the UI widgets that will need to be accessed in event listeners. Luckily, the problem is remedied by a quick keyboard shortcut.


Image Avoid introducing fields that contain valid data only temporarily; it can be rather tricky to ensure that all accesses will actually find valid data. If you find that you have several temporary fields, try to extract them to a separate class, together with the methods that use them. Very often, you will find that you are actually applying the STRATEGY pattern and are encapsulating an algorithm in an object.


1.3.8 Static Fields

The first rule about static fields is a simple one: Don’t use them.


Reserve static fields for special situations.


Image 1.1Static fields go against the grain of object-oriented programming: They are associated with classes, not objects, and classes should be an irrelevant, merely technical necessity. While you can reuse functionality in objects by creating just another instance, you cannot clone classes. That is, a class exists once per JVM.3 When you find that you are using static fields very often, you should probably rather be writing in C (and even good C programmers shun global variables). However:

3. Technically, this is not quite true: A class is unique only within a class loader, and different class loaders may well load the same class several times, which leads to separate sets of its static fields coexisting at runtime. You should never try to exploit this feature, though.


Do use constants.


Fields declared as static final are constants, meaning named values. They are not associated with classes at runtime, the compiler usually inlines them, and they disappear from the binaries altogether. Whenever you find in the code a stray literal value that has some meaning by itself, consider introducing a constant instead.


Tool: Extract Constant

From the Refactoring menu (Alt-Shift-T), choose Extract constant.


It is sometimes inevitable to have global data for logical reasons. That is, when running Eclipse, there is only one Platform on which the application runs, one ResourcesPlugin that manages the disk state, and one JobManager that synchronizes background jobs. In such cases, you can applyImage 7.10 the SINGLETON pattern. It ensures at least that clients keep working with objects and the implementation itself can also refer to a proper object.Image 100


Pattern: Singleton

If for logical reasons a particular class can have only one instance, then ensure that only one instance can ever be created and make it accessible globally.

To implement the pattern, maintain a static instance (line 2) with a global getter (line 4) and make the sole constructor private (line 3) to disable the creation of further instances. Lazy creation (line 6) can increase efficiency, since very often singletons are rather comprehensive classes.


1 public class Singleton {
2 private static Singleton instance;
3 private Singleton() {}
4 public static Singleton getInstance() {
5 if (instance == null)
6 instance = new Singleton();
7 return instance;
8 }
9 }




Image Be very conscientious about the logical justification of single instances. Singletons may appear an easy way out if you have forgotten to pass around some requiredImage 226 object and need just any instance quickly. Don’t yield to that temptation: You lose the benefits of object-oriented programming, because your object’s fields are essentially global variables.



Image Image 163As a special implication, singletons make testing harder, because it is not possible to change the type of created object. Tests have to work with exactly the sameImage 5.3.2.1 object as the production scenario. It is not possible to provide mock objects in creatingImage 5.3.2 a simplified fixture, so that its unit tests no longer run in the minimal possible context.



Image Image 8.1Be careful with SINGLETONS in multithreaded applications: When two threads invoke getInstance() simultaneously, it must be guaranteed that only a single instance is created. You can, for example, make the getInstance method synchronized, which uses the class’s built-in lock. Beyond that, the singleton object itself must, of course, be thread-safe. If you feel you need a global object in a multithreaded environment, youImage 148 can also consider using thread-local variables, which are supported in the Java library by class ThreadLocal.


1.4 Methods

If objects are to be active, collaborating entities, it is their methods that must make them active and that enable them to collaborate. In other words, simply invoking a method on an object is sufficient to induce the object to perform actions, compute results, or make decisions. Understanding methods is therefore key to understanding objects. We will also discuss manyImage 100 design patterns already in this section, because their intention is best motivated and explained from the behavior of the occurring methods.

1.4.1 An Object-Oriented View on Methods

In most presentations of object-oriented programming, methods are linked tightly with classes. You will know the approach: Classes define methods, the classes get instantiated, and one can invoke the defined methods on the instances.


public class BaseClass {
public int op(int x) {
return 2 * x;
}
}



BaseClass obj = new BaseClass();
System.out.println(obj.op(18));


Object-oriented programming is then characterized by polymorphism: One can define derived classes (or subclasses), and override the methods introduced in the base class (or superclass).


public class DerivedClass extends BaseClass {
public int op(int x) {
return x + 24;
}
}


The term polymorphism captures the fact that an object appears to have several “shapes.” An object of the derived class can be used wherever the base class is expected. The object appears to have acquired a new shape. The transition from shape DerivedClass to shape BaseClass is explicit in the call to method work() shown next. Technically speaking, we must distinguish between the static type of a variable or field, as declared in the source code, and the dynamic type, which is the class instantiated at runtime to create the contained object. Under the typing rules of Java, the dynamic type is always a subtype of the static type (where subtyping is reflexive, meaning it includes the case of the types being the same).


public void workWithObjects() {
DerivedClass obj = new DerivedClass();
work(obj);
}
public void work(BaseClass obj) {
System.out.println(obj.op(18));
}


We will not go into the details—you already know this material by heart.

To acquire a truly object-oriented coding style, it is, however, best to start out from a radically simple point of view:


Methods belong to the object, not to the class.


The language Smalltalk makes this view explicit: One does not “invoke” aImage 123,109 method at all, but rather sends a message to the object and the object itself decides which method it will execute in response. The object-based language JavaScript takes an even more literal approach and says thatImage 85 “methods” are merely functions stored in an object’s fields. In Java, the previously described simplification has the virtue of deliberately blurring the technical details of inheritance hierarchies: No matter how the method ended up in the object, the important thing is that it exists and can be invoked.Image 111


Image You will appreciate the simplicity of the formulation if you follow the details in the Java Language Specification. The core is in §15.12.4.4, which defines the dynamic method lookupImage 239 procedure in method invocations, or more precisely virtual method invocations. Dynamic method lookup involves both the runtime class R of the target object and the compile-time type X of the object reference used to invoke the method m. It searches classes S, starting with R and traversing the hierarchy upward, until the central case applies: “If [ ... ] the declaration in Soverrides (§8.4.8.1) X.m, then the method declared in S is the method to be invoked [ ... ].” Not only does this definition involve the interaction of several concepts, but it also gives only a procedural description of the lookup through a linear search in the inheritance hierarchy. If you think through these steps for every single method call, you will never get any coding done.


For practical purposes, the following understanding is sufficient:


When a class declares a method, then this method will be stored into all of its instances.

Furthermore, a class inherits all methods declared in its superclass that it does not declare itself.


This formulation has the advantage that the runtime behavior remains simple: To find the invoked method, just look what has ended up in the object. The usual Java terminology is just a more detailed version:

Image 111(§8.4.2)A method’s signature consists of its name and parameter types.

• If a superclass happens to have declared a method with the same signature, then that method is said to be overridden.

• If a superclass or interface happens to have declared only an abstract method with the same signature, then that method is said to be implemented.

Method overriding replaces an inherited method with a new, completely independentImage 3.1.1 version. Again, however, the important point is not the process, but the result:


Callers are not aware of the replacement of a method by overriding.



Image Image 111(§15.12.4.4) The technically minded reader may find it helpful to know that this is what actuallyImage 3,19,239 happens internally: The JVM keeps a method dispatch table per class, which has a slot for every method declared in the hierarchy. Each slot contains a code pointer to the currently valid method; overriding replaces the code pointer. To invoke a method, the JVM merely looks up the table from the object, then the code from the table. C++ uses the term virtual table for the same concept.



Tool: Override Method

To override one or more methods, choose Source/Override or Implement Methods (Alt-S V) and follow the steps in the dialog. For single methods, you can also type the name of an inherited method in a derived class, auto-complete it, and choose Override Method.


A second, more advanced tool takes care of slips in methods’ signatures. It is often the case that you want to add new parameters—for instance, if a generalized implementation of a method requires more information, or if design changes require further collaborations and passing along further objects.


Tool: Change Signature

To modify a method’s signature consistently throughout the code base, place the cursor in the method’s header, or its name at a call site, and use Alt-Shift-C (or Alt-Shift-T and the refactoring menu).


Finally, let us look at a special aspect of methods:


Use methods to enforce encapsulation.


Since the fields containing an object’s state are its private concern, methodsImage 1.3 Image 1.1 are the only way of accessing the object at all. At the same time, the methods’ definition determines whether the fields really remain private. We have seen some pitfalls when passing and returning internal objectsImage 1.3.3 by reference, which enables clients to modify these internals. As a positive example, think back to the GapTextStore: Its public methods present aImage 1.3.1 consistently simple view of a “stored text document” and never mention any aspects of its internal organization. You should always strive to write methods that do not allow the client even to guess at what goes on inside the object.

1.4.2 Method Overloading

It remains to introduce briefly a second language feature concerning methods: overloading. Overloading is completely orthogonal, or complementary, to the question of overriding: The two can be used in isolation or can be combined arbitrarily.

Compared to overriding, overloading is very simple and happens completely at compile-time. If an object contains two methods with the same name, but different signatures, then the method is said to be overloaded. For a method call, the compiler resolves the overloading by choosing theImage 111(§15.12.2) “closest match” based on the types of the arguments. A method call in the created class file does not contain just the method’s name, but its entire signature. Consequently, there is no longer any ambiguity and no need for runtime choice. The usage of overloading is simple:


Overloading expresses a close logical relation between methods.


For instance, the Eclipse API for working with resources in the workspace offers two methods for obtaining a file from a surrounding folder:

org.eclipse.core.resources.IFolder


public IFile getFile(String name);
public IFile getFile(IPath path);


Indeed, the two methods are so closely related that the results of the following calls are the same (by equals(), since resource objects are handles):


IFile resultByString = folder.getFile("b/c");
IFile resultByPath =
folder.getFile(Path.fromPortableString("b/c"));


A special form of overloading is convenience methods, to be discussedImage 1.4.4 shortly. They represent simplified versions of very general methods.


Image You should take the logical connection implied by overloading seriously: The reader expects that basically the same thing will happen in all variants of the method.Image 1.1 Image 1.2.3 Image 1.4.3 Think from the callers’ perspective and aim at keeping their code readable. When in doubt, add some suffix to the name to disambiguate it.



Image Overloading and overriding interact at one special point: Overriding takes place only if two methods have the same signature. Up to Java 1.4, a common source of errors was that a method seemed to override an inherited one, but introduced a small variation in the parameters, so that the old method remained in place. The @Override annotation reduces that risk, since the compiler can at least check that some method has been overridden.


1.4.3 Service Provider

The first and most fundamental approach to using methods is to considerImage 1.1 them as services offered by the object to the outside world. Other objects can collaborate by invoking these methods, and can make use of the object’s functionality instead of reimplementing it. Foremost, services are offered as public methods, because these are accessible to any other object in the system.

As a simple example, an IFile object in the Eclipse platform offers the following method to write data to disk. The data is given as an Input Stream, from which the bytes are read. Additional parameters specify whether changes not yet noticed by Eclipse should be ignored and whether a history entry for undo should be created. In the background, the object performs the disk updates, changes the internal resource data structures, and sends out change notifications—a complex service provided to the client object for free.

org.eclipse.core.resources.IFile.setContents


void setContents(InputStream source, boolean force,
boolean keepHistory, IProgressMonitor monitor)


Further typical examples are found in the class GC from SWT, which enablesImage 7.8 drawing on the screen. They demonstrate the idea of “services” very well, because simple calls by the client require complex interactions with the operating system in the background. For example, just passing a position and a string to drawString() will take care of all font issues, such as rasterization and measuring.

When writing service provider methods, the fundamental guideline is:


Always think of methods from the clients’ perspective.


A service method will be written once, but will potentially be used in many places throughout the code base. It is therefore essential to keep its use simple, even if this leads to a more complex implementation. Simplicity can often be gauged by a few basic questions: How many parameters must be provided? Is their meaning clear? Can the method’s effect be describedImage 4.1 in 1–2 sentences? Are there complex side conditions on the use? It is alsoImage 5.4.3 very helpful to write out a few use scenarios, possibly in the form of unit tests, to get a feeling for what the client code will look like, whether the code “tells its own story.”Image 1.2.3

Designing service methods always involves decisions. For example, consider again GapTextStore, which handles text documents for editorsImage 1.3.1 throughout Eclipse. Its core functionality is provided by a method replace (pos,len,txt), which performs the obvious text replacement. The Swing counterpart GapContent, in contrast, offers two separate methods insert String() and remove(). Both alternatives are certainly viable. While Swing’s choice may be more natural from an application perspective, SWT’s choice is more uniform from a technical point of view and also leads to further uniformity in other objects. For instance, undo/redo can be implementedImage 9.5 in a single command class, instead of two. In any case, the crucial consideration is the expected use of the methods, not their implementation.

Certainly, not all public methods perform complex services. Sometimes, the services are extremely simple, such as accessing a property field. It isImage 1.3.3 Image 1.3.4 nevertheless necessary to consider such methods as full service providers. In particular, you should ask yourself—again—whether these methods should actually be public. Furthermore, they may require new functionality alongImage 2.1 the way—for instance, they may notify observers about the change, or may maintain internal consistency conditions with other fields.

So far, we have looked at public methods, which are offered to the overall system. Objects usually also provide services for more restrictedImage 3.1.2 audiences. Services to subclasses are usually given by protected methods and require special care in calls, since the caller must understand more ofImage 9.3.2 the object’s implementation. For instance, an AbstractListViewer maps linear data structures into lists or combo boxes on the screen. Internally, it keeps a mapping between data items and screen texts. The mapping is considered an implementation detail in general, but subclasses can still access it through methods like indexForItem(). Similarly, objects in aImage 1.7 package sometimes collaborate closely, and their services to each other are presented as default visible methods.

1.4.4 Convenience Method

Sometimes a service method is so general that calls become rather complex. When restricting the generality is not an option, it may be useful to provide convenience methods, which are sufficient for the most frequent applications and which internally delegate to the general method.

For instance, a tree widget on the screen can be told to expand the topmost levels of the displayed data structure through the following method expandToLevel. Really, this delegates to a more general method that expands below a given node:

org.eclipse.jface.viewers.AbstractTreeViewer


public void expandToLevel(int level) {
expandToLevel(getRoot(), level);
}


Similarly, a Document manages a text document with positions, which are markers that move according to modifications. The following convenience method allows simple clients to forget that there are actually several different categories of positions:

org.eclipse.jface.text.AbstractDocument


public void addPosition(Position position)
throws BadLocationException {
addPosition(DEFAULT_CATEGORY, position);
}


Whether to introduce convenience methods is, however, a decision not to be taken lightly. Having too many clutters up the API that users of the classImage 7 have to learn. Worse, the semantics may become unclear. In swt.Control, for example, the general setBounds(rect) makes the widget cover a rectangular screen area; unexpectedly, setLocation() not only moves the widget, but also re-sizes it to width and height 0 (which is not stated in the documentation). It may also become unclear which method is the real implementation, so that it becomes hard to change the behavior by overriding.Image 3.1.7 For instance, Figures in the Graphical Editing Framework have methodsImage 214 setBounds() and setLocation(); code inspection reveals that the latter is a convenience method for the former. However, from the documentation alone, the first might also call setLocation() and setSize().


Image To clarify which one is the core method and to avoid confusion in overriding, you can make the convenience method final (in the example from Figure, null means “no layout constraint” and -1 means “at the end”):



public final void add(IFigure figure) {
add(figure, null, -1);
}


Convenience methods should not be introduced for a particular client’s convenience, but rather created only if they are useful as an additional interface. Their design is subject to the same considerations as that of general service methods.

1.4.5 Processing Step

The services of an object are rarely so simplistic that their implementation fits into the service provider method itself. Methods that span several screen pages are a maintenance nightmare, as they are write-only code. Realistically, once you get the test cases running, you will be only too happy to free your mind from the burden and move on to the next task. Then you have to keep hoping that the code will never be touched again.

Here is a typical example of how to get it right. Opening a new window on the screen seems simple enough: The client just calls open() onImage 9.3.2 a Window object. The implementation shows the complexity: The window object negotiates with the operating system for resources, makes sure thatImage 7.1 the result fits on the screen, shows the window, and finally dispatches allImage 7.10.1 events to the new window, if desired.

org.eclipse.jface.window.Window


1 public int open() {
2 create();
3 constrainShellSize();
4 shell.open();
5 if (block) {
6 runEventLoop(shell);
7 }
8 return returnCode;
9 }


This code shows good structuring; you can grasp the essence of the solution without knowing any of the details.


Use methods to break up the implementation into meaningful steps.


The steps are usually part of the object’s implementation. That is, they are internal and therefore declared private or protected. Making them private clarifies their auxiliary status and there are no further difficulties. Making processing steps protected, in contrast, needs some reflection:Image 1.4.8 Image 3.1.2 Can subclasses reuse the steps for their own purposes? Then the methods become part of the subclass interface, which needs extensive considerationImage 1.4.11 in itself. Or do you want subclasses to influence the behavior, by extensionImage 1.4.9 or according to the TEMPLATEMETHOD pattern? All of those questions will be discussed in due course. For now, it is sufficient to summarize:


Take care not to expose internal processing steps inadvertently.



Image Sometimes breaking up a method into smaller steps does not work out, because it requires complex parameter passing or temporary fields. In such cases, it is useful toImage 1.4.8.3 Image 1.8.6 Image 27 factor out the method into a separate object according to the METHOD OBJECT pattern.


A more fundamental question is how to identify useful processing steps in the first place. The simplest approach is to allow yourself some wishful thinking: While coding some functionality that you know to be complex, just look out for bits that you know to be solvable, even if that resolution requires some further effort. Choose a descriptive name and just write down a call that you would think sensible, even though you know that the method does not yet exist.


Tool: Create Method from Call

Place the cursor on the closing parenthesis and activate Quick-Fix (Ctrl-1). Eclipse will create a method below the current one that fits the call. Use Tab to jump to the linked positions at which Eclipse has made choices—for example, of parameter types and names—to amend those choices quickly.


The problem with wishful thinking is that it requires experience. In other words, you can think of useful steps only if you recognize a familiar pattern—that is, if you have coded something similar before. Making the right wishes can be surprisingly hard.

A complementary approach is to recognize processing steps after the fact, either in existing code or while you are coding. Eclipse’s extensive tool support in this area testifies to the practical importance of this strategy.


Tool: Extract Method

Select statements or a (sub)expression in the Java editor, then invoke the Refactoring/Extract method (by Alt-Shift-M, or through Alt-Shift-T). A pop-up dialog enables you to customize the result.



Image Eclipse is really amazingly intelligent: If it detects that some statement has a side effect on a local variable, it passes the current value as a parameter and returns the variable’s new value from the method.


Tools, especially powerful tools, always need to be applied with care and consideration. Suppose that in the context of a spreadsheet application,Image 9.4 you were to write a method that gets the cell content of a particular cell, ensuring that the cell exists. The row and column are given in 1-based, user-oriented notation. You may come up with the following code (cells is a HashMap):

methods.refactoring.ExtractExampleCells00


1 public String getCellContent(int userRow, int userCol) {
2 Cell c = cells.get(new CellRef(userRow - 1, userCol - 1));
3 if (c != null) {
4 c = new Cell();
5 cells.put( new CellRef(userRow - 1, userCol - 1), c);
6 }
7 return c.content;
8 }


When you extract lines 2–6, you get a method getOrCreateCell with a tangle of functionality: It shifts indices to internal notation, adds cells as required, and so on. Very often, you have to clean up the code before extracting methods, and in many cases, a second tool comes to the rescue.


Tool: Extract Variable

Select an expression and bind its result to a local variable by Alt-Shift-L (or refactor with Alt-Shift-T; or Quick-Fix with Ctrl-1). Variable extraction can also replace multiple occurrences of an expression simultaneously.



Image To select an expression for extraction, it is sufficient to place the cursor onto a character that belongs to none of its subexpressions, such as the operator in a binary expression.


For instance, if you want to capture only the on-the-fly creation of new cells, you would extract the CellRef objects into a variable and would end up with this code:

methods.refactoring.ExtractExampleCells01


public String getCellContent( int userRow, int userCol) {
CellRef key = new CellRef(userRow - 1, userCol - 1);
Cell c = getOrCreateCell(key);
return c.content;
}


If you like this better, you can immediate inline the parameter, and the variable c as well.


Tool: Inline Variable

Select a variable declaration or a variable reference and press Alt-Shift-I (or Alt-Shift-T for the menu).


This leaves you with the following neat code:

methods.refactoring.ExtractExampleCells02


public String getCellContent(int userRow, int userCol) {
return getOrCreateCell(
new CellRef(userRow - 1, userCol - 1)).content;
}


Looking at this code, you might see another piece of useful functionality lurking inside, and obtain the following final version. Note also how introducingImage 1.2.3 an additional method makes the code “tell its own story.”

methods.refactoring.ExtractExampleCells03


public String getCellContent(int userRow, int userCol) {
return getOrCreateCell(convertExternalCoordinates(userRow,
userCol)).content;
}


This example demonstrates a general guideline:


Plan method refactorings carefully and judge the result by readability.


Nevertheless, you will note that modifying code with the Eclipse tools is fundamentally different from writing code. The code is no longer a stiff, unmodifiable, fixed mass of statements, but becomes malleable, so you can easily mold or massage it into new shapes at the expense of a few keyboard short-cuts. Also, an undo will revert all modifications simultaneously, which leaves you free to experiment until you are satisfied with the result.

Despite the ease of code editing and experimentation, the original goal for introducing methods as processing steps remains valid:


Identify processing steps from the meaning and context of the code.


Methods, even internal helper methods, are never merely technical. Always be aware that methods structure your solution and shape the way that you, and later your team, will think about the solved problem.

Going one step beyond, this thought touches on the old debate about whether code should be written bottom-up, from the smaller steps to the larger, or top-down, by wishful thinking, from the general strategy to the details. In practice, the question arises very rarely, because both require experience with similar problems solved. When solving new problems, your first concern is to find and understand any solution. In the words of Tim Budd:Image 55(§2.3)

[ ... ] design might be said to progress from the known to the unknown.

In the extreme, this might actually lead to bottom-up code development, if you are working in an area that you are very familiar with. If you know that a particular bit of functionality will come in useful, you should not hesitate to write it down as a building block for the later development.


Write down what you clearly understand is necessary.


In summary, Eclipse’s powerful tool support enables you to start coding,Image 92 perhaps along test cases to be solved, and later restructure the code toImage 5.2 facilitate maintenance. We close the discussion on methods as processing steps with one final guideline, which holds regardless of the way by which you have arrived at your code:


Don’t forget to clean up your code once you have understood the solution.


1.4.6 Explanatory Methods

Method names are extremely powerful documentation. In the declaration,Image 1.2.3 they head the method body and leave the reader with an anticipation, an expectation of what that code will probably achieve. At the same time, the method name appears at the call sites. There, it gives a summary of a particular computation that needs to be performed. Methods can therefore be used as named pieces of code.


Use method calls to express and document your overall solution strategy.


This advice is actually at the heart of agile software development. ThisImage 28 Image 172 approach encourages you to cut down on wasted work. For instance, rather than writing documentation that no one reads or updates, it is more sensible to put an extra effort into creating good code structure to make the code self-documenting. Having short methods with descriptive names is a first, fundamental step.


When tempted to write an inline comment, introduce a method instead.


To see what can be achieved, consider the following excerpt from the Gap TextStore.Image 1.3.1 To prepare for an insertion, it can just move the gap, or else it must reallocate the backing array because of insufficient space. The core of the method exhibits that choice, and also the contained parallelism, beautifully:

org.eclipse.jface.text.GapTextStore.adjustGap


if (reuseArray)
newGapEnd = moveGap(offset, remove, oldGapSize, newGapSize,
newGapStart);
else
newGapEnd = reallocate(offset, remove, oldGapSize, newGapSize,
newGapStart);


Introducing explanatory methods also does not require deep thoughtImage 12 or weighing of alternatives. The goal is not reusability or extensibility, but merely a place to put an expressive name. Making the methods private ensures that no one learns about their existence and that you remain free to restructure your code if you happen to find a better solution later on. Don’t hesitate to extract methods with the tools Extract Method and Extract Variable for the purpose: Having a good, explanatory name is better than having no name at all. And in case you ever follow a dead-end street, there is always a simple solution:


Tool: Inline Method

Place the cursor on a method name and press Alt-Shift-I to inline the method’s body (or use Alt-Shift-T for the Refactoring menu).


1.4.7 Events and Callbacks

Very often methods defined by an object are not services or functionality offered to others, but rather points where the object accepts notificationsImage 2.1 from others. Observers are interested in state changes of other objects;Image 2.3.2 visitors are called back for each node encountered in a hierarchical dataImage 7.3 structure. In the context of frameworks, inversion of control dictates that the framework decides when to run which application code. Callback methodsImage 3.2.6 are usually defined in interfaces and are then implemented in different objects. Examples can be found in the corresponding sections.

Technically, there is nothing special about a callback: The method gets called and the object executes the code to update its state and carryImage 7.11 out suitable operations. Conceptually, however, callback methods cannotImage 1.1 Image 1.4.3 be understood from the caller’s perspective, but only from the object’s perspective—that is, the object alone determines the suitable reaction to the method call. Furthermore:


Understanding callbacks requires understanding the framework’s mechanisms.


You also need complementary knowledge about the framework’s mechanismsImage 1.4.9 Image 3.1.2 to understand when and why the method will be called. The method achieves its purpose only because the environment makes guarantees about the circumstances under which it will call the method. For instance, the JFace viewers display application data on the screen. They use contentImage 9.3.2 providers to access that data. These must provide, among other things, the following method:

org.eclipse.jface.viewers.IContentProvider


public void inputChanged(Viewer viewer, Object oldInput,
Object newInput);


To properly understand and implement this method, you have to know that it will be called whenever setInput() is invoked on the viewer, that oldInput can be null (for the first call), and that there will be one last call with newInput==null when the display widget is removed from the screen. That last case is important to de-register any observers that theImage 2.1.2 content provider may have registered. The method name alone does not always tell the whole story.

1.4.8 Reused Functionality

Code reuse is a fundamental and continuous challenge in software development: We simply cannot afford to recode a solution whenever we re-encounter a known problem. At a technical level, methods form the basis of reuse; they contain the solution’s code and make it available as a service to any client that requires it. This approach to reuse raises the deeperImage 12.4 question of which object, or class, should contain the code—that is, where a solution to a recurring problem is best placed to be as widely available as possible. Of course, the answer goes beyond single methods, to objectsImage 1.8.6 Image 1.7 or even subsystems, if the solution requires more infrastructure.Image 243

For the moment, we will start with methods. Also, in practice, you will often be faced with this situation: You see a method that implements a functionality that you require—how can you reuse it without reverting to copy-and-paste? The different answers to that question structure the subsequent discussion.

1.4.8.1 Reuse Within a Class

At the most basic level, an object offers the reusable functionality only toImage 1.4.5 itself. Technically, the methods are private, or protected if they are to be available within the inheritance hierarchy. As a tiny but typical example, the developers of GapTextStore have realized that the representation ofImage 1.3.1 the gap by indices gapStart and gapEnd may not be ideal, since in many situations the representation by gapStart and gapSize would be more adequate. They have therefore introduced a reusable service that converts to that representation on the fly, rather than inlining the computation, which would be simple enough.

org.eclipse.jface.text.GapTextStore.gapSize


private int gapSize() {
return fGapEnd - fGapStart;
}


1.4.8.2 Reuse Through Inheritance

The availability of protected methods throughout the inheritance hierarchy was considered a prime motivation for object-oriented programming in the early days. When developers required new functionality, they wouldImage 3.1.7 simply extend a suitably close class in the library and then have to provide only the missing bits, building on the existing infrastructure.

As an example of such reuse, consider StructuredViewer, which is aImage 9.3.2 base class for many of JFace’s viewers, including lists, tables, and trees. Each such viewer maintains an association between application data items and widgets on the screen, such as rows in lists, tables, and trees. A fundamental service then is to map a data item to its widget. The corresponding protected method findItem() is called at roughly 20 places throughout the hierarchy.


Achieving reuse requires careful planning and broad experience.


Image 1.4.5If it was hard to identify suitable processing steps, it is harder still to identify reusable services, methods that will be useful beyond the contextImage 132,130,131,71 in which they were invented. This task requires experience as well as forethought; it requires an overview of common problems, their best solutions, and an anticipation of their usage. What is more, there is a high danger ofImage 92 spending a lot of effort on “reusable,” “general” solutions that will turn out not be used in the end. The task is daunting, perhaps too daunting to be attempted.

Fortunately, agile software development has taught us not to aspire toImage 28 the one, ultimate solution immediately. Rather, it advocates that we justImage 92 “do the simplest thing that could possibly work.” If later on that solution turns out to be no longer good enough for the newest challenge, you canImage 1.2.2 always refactor it.

For instance, suppose you have a fairly complex private processing step in one of your classes, which works with several fields and invokes several sub-steps. The class was never intended for reuse, and it does not yet have subclasses or a superclass (apart from Object, of course). But the code is readable, stable, and general, and you want to reuse it. Inheritance can be put to use as shown in Fig. 1.3: You move the reusable code to a place where it can be accessed from outside its current context, by introducing a superclass. Eclipse makes it simple to implement this solution:


Tool: Extract Superclass

In the Java editor, choose Extract Superclass from the Refactoring menu (Alt-Shift-T). Choose the members that embody the desired functionality. The preview within the dialog is useful for detecting broken dependencies and fixing them by extracting further members. If a class already has an explicit superclass, the tool pushes the new class in between the two.


Image

Figure 1.3 Extracting a Superclass for Reuse

If you already have a class hierarchy set up and later realize that some processing step is more generally useful, you can invoke a more specialized extraction on methods and fields only.


Tool: Pull Up

In the Java editor, choose Pull up from the Refactoring menu (Alt-Shift-T). In the dialog, select the members to be moved up. The member in which the cursor was placed is preselected.


As an example, the classes MouseController and KeyboardControllerImage 9.2 in the online supplement were actually created in this way, starting from the first one. Looking at the code, and in particular their superclass Controller, you can still trace the process.

Note that, as in the case of processing steps, the ability to massage and mold code into place by powerful tools does not free us from careful planning: we were able to extract the Controller only because we had written a few dozen controllers previously and knew their commonalities.Image 71


Focus on conceptual commonalities, extract them to methods, and then to superclasses.


The Eclipse tools do not provide any intelligence; that part remains with the developers. They merely free developers from the technical chores and enable them to focus on conceptual decisions.

1.4.8.3 Reuse Through Separate Objects

Image 3.1.2 Image 3.1.11 Image 232Although reuse through hierarchies may appear particularly “object-oriented,” and hence nifty or desirable, it is by no means the only approach to reuse. Indeed, it is not even the best option, since it leads to rather severe complications: It fills the single available superclass slot andImage 3.1.4 Image 3.1.2 couples the classes in the hierarchy very tightly. Modifying a base class canImage 3.1.11 easily break the subclasses, so you may not be able to adapt the class to new challenges.

Let us therefore start again: You recognize that some useful, complex functionality already exists in some method, which unfortunately uses other helper methods and several fields. An alternative strategy then is to extract the functionality into a completely separate object. Fig. 1.4 gives the desired result, and Eclipse provides just the tools to obtain it.

Image

Figure 1.4 Extracting an Object for Reuse


Tool: Extract Class

Invoke Refactoring/Extract Class (Alt-Shift-T) and select the fields on which the functionality will work. The tool will introduce a helper object in a new field and redirect all references to the extracted fields to that helper.


Compared to the Extract Superclass dialog, this one may be disappointing: You cannot even extract the methods you were interested in! Fortunately, Eclipse remedies this with a second tool:


Tool: Move Method to Helper

Place the cursor into a method and invoke Refactoring/Move (Alt-Shift-V) to move it to the extracted helper object. Again, the tool will update the references within the method and to the method accordingly. You can also keep the original method, if other clients wish to refer to it and you do not want to publish the helper object (even though you will publish the helper class).



Image In Eclipse 4.3, you cannot move fields to the helper object in the same way. You have to extract them in the initial step.


When you follow these steps, you obtain a completely independent objectImage 1.8.5 Image 1.8.6 that acts as a container for the desired functionality. This solution is actually closer to the guidelines on objects than the one based onImage 1.1 inheritance: The extracted object is small and self-contained; its fields and methods are not lumped into the objects that require its services. Also, the object can be used more flexibly; you can even create temporary instances on the fly, within arbitrary methods.

Another way of looking at the newly created object is to say that it encapsulates a method or algorithm. This leads to the more general idea of capturing a complex piece of code as a separate object with the aim ofImage 1.8.6 making it more self-contained and understandable.

1.4.8.4 Reuse Through Static Methods

In rather rare cases, the functionality to be reused is an algorithm or a mathematical calculation that does not relate to any hidden state in some object. Examples are Collections.sort(), Array.binarySearch(), and Math.sin(). Furthermore, utility methods often take this form, such as SwingUtilities or Geometry in SWT.

In principle, however, static methods are not properly object-oriented: They do not belong to objects, but rather to classes. If some later extension of the functionality happens to require fields after all, you have to rearrange your software quite severely, by passing along suitable object references. TheImage 1.3.8 general guideline, as with static fields, therefore is:


Reserve static methods for special occasions.


When considering whether to introduce a static method, always recall theImage 210 bon mot and ask yourself: Am I perhaps being a FORTRAN programmer writing my FORTRAN program in Java?


Image A fundamental conceptual reason for avoiding static is that classes in Java are second-class citizens: The objects accessible through .class or getClass() do not exhibit dynamic dispatching, they cannot implement interfaces, and so on. In contrast,Image 109(Ch.16)Smalltalk starts from the premise that “everything is an object”—even the classes. The question follows: What is the class of a class? It is a meta-class, which defines the (static) methods of the class, and inheritance on classes induces inheritance on their meta-classes—in consequence, one can override static methods. The flexibility ends, however, one step further: The class of all meta-classes is MetaClass, a fixed entity, just like Class in Java.


1.4.9 Template Method

Image 1.4.5We have examined the idea of wishful thinking for inventing processing steps: While writing some larger method, you postpone implementing the details by introducing methods as placeholders. You can do this because you know that there is a suitable implementation, but writing it down would sidetrack you at this point. One step beyond, you may be aware that there are several useful implementations of a step, each of which would give rise to a useful variant of the overall algorithm. This is a typical scenario forImage 70 introducing a TEMPLATE METHOD: You write a method that is merely aImage 100 template, a blueprint where some steps are intentionally left out.


Pattern: Template Method

Template methods provide the main structure of a computation, while keeping some steps flexible to cater to different application scenarios.

1. Implement the blueprint of a service in a base class.

2. Make steps that allow different implementations into protected abstract methods.

3. Introduce different derived classes to fill in the variants of the steps.

You may also choose to provide default implementations for some steps. This reduces the effort that clients have to spend in simple cases.



Image Introducing a method as abstract is a generally useful trick to force subclasses to decide on an implementation. Also, Eclipse will guide the developer a long way: It will display an error and will also offer to Quick-Fix it by declaring the missing methods. The abstract tag here serves as a technical reminder of missing functionality.


Image 9.3.2As an example, consider JFace’s AbstractListViewer. It provides the infrastructure for displaying application data in simple widgets such as lists and combo boxes. A central task is this: Whenever the application data changes, the corresponding display must be updated. Toward that end, one must determine the row in the list, compute the new text to be shown, and finally display that text. All but the last step in lines 7–8 are generic; that is, they work for any kind of list:


1 protected void doUpdateItem(Widget data, Object element,
2 boolean fullMap) {
3 if (element != null) {
4 int ix = getElementIndex(element);
5 ILabelProvider labelProvider =
6 (ILabelProvider) getLabelProvider();
7 listSetItem(ix, getLabelProviderText(labelProvider,
8 element));
9 }
10 }


The only place where the kind of display widget does matter is the actual update of the displayed text. That is left to be implemented in subclasses:


protected abstract void listSetItem( int index, String string);


The example also shows that experience and analysis are necessary in applying the pattern: Passing the text to be displayed might be an easy deduction, but how can you be sure that an index alone will be sufficient information for actually placing it at the correct position? That knowledge can be gained only by looking at the API of concrete SWT widgets—that is,Image 7.1 List, Combo, and CCombo. All of them offer a setItem(pos,text) service method, so that the different variants can, in fact, be implemented directly.


Image The example indicates not to use TEMPLATE METHOD lightly. To emphasize the point, if you take the pattern to the extreme, you would write methods that consider each substep as a possible point of variation. You would end up with classes that can be adapted flexibly to different situations, just by introducing subclasses. You would, inImage 7.3.3 fact, be creating a white-box framework. At the same time, the danger of introducingImage 92 and paying for “Speculative Generality” would be extremely high.


Since template methods are extremely common, Eclipse provides support for creating them. In a first scenario, you might find that method reuseImage 1.4.8.2 through a common superclass does not quite work out, because some parts of the method must differ between the original and the new calls.


Tool: Pull Up for Templates

The tool Pull Up (Alt-Shift-T) allows you to pull up methods as abstract declarations only, through the action Declare abstract in destination. To introduce a TEMPLATE METHOD, pull it up as usual, but pull up some of its steps as declarations only. The same applies to the toolExtract Superclass.


A second scenario occurs when you have a general method in a base class that has so far worked uniformly for all subclasses in your system.Image 3.1.5 At some point, however, a new subclass (i.e., a new special case) requiresImage 1.4.5 a slightly different behavior. You can first use Extract Method to factor out the changed behavior into a separate processing step. Then, you can override the method in the new subclass. In effect, you have created a TEMPLATE METHOD with a default implementation.

One step further, you might also notice that, in fact, many subclasses require different implementations of the step. To force their developers to make a decision, you wish to make the step abstract in the base class, and to push down the default implementation into the existing subclasses. Eclipse enables you to do just that:


Tool: Push Down

To enable different implementations of a method in the hierarchy, place the cursor in the method and choose Refactoring/Push down (Alt-Shift-T). When you choose Leave abstract declaration as the action, you effectively turn all callers of the method into template methods.


1.4.10 Delegated to Subclass

Image 48 Image 3.1.5A classical usage of inheritance involves modeling of abstraction hierarchies: The base classes capture some abstract concept that exhibits some general behavior and promises general services to its clients, while the derived classes represent special cases that will implement that behavior according to their own specific natures.

In this context, it is natural that some of the services cannot be implementedImage 9.3.2 at all at the general level. For instance, JFace introduces the abstraction of a viewer, which maps application data into display widgets on the screen. Any type of viewer takes some data structure as its inputand, among other things, enables the user to select items from the data structure. The following two methods capture this promise to the client, yet they are abstract because text viewers and structured viewers, such as lists or tables, require completely different implementations.

org.eclipse.jface.viewers.Viewer


public abstract void setInput(Object input);
public abstract ISelection getSelection();


Image 2.3A further field for such delegated services is the COMPOSITE pattern, where recursive operations are declared in the root class of the hierarchy and are implemented in each concrete subclass. This structure enables clients to work uniformly with any objects in the composite structure.

1.4.11 Extending Inherited Behavior

In general, method overriding is used to adapt an object’s behavior to fit its special purpose. The adaptation is usually arbitrary. There is one specialImage 3.1.7 Image 6.4.1 case, though, that is often found in abstraction hierarchies: The overriding method must only add to, not replace, the behavior of the overridden method. In other words, the superclass relies on its own method being called by the subclass.

A typical example is found in the hierarchy of JFace viewers. TheyImage 9.3.2 take a widget on the screen and augment it to display application-specific data. Toward that end, they must initially hook themselves to the widget. The class ContentViewer therefore introduces a methodhookControl(), which performs the necessary work. Subclasses add further hooks according to their needs. A StructuredViewer, for example, must react to double-clicksImage 7.1 and similar actions that indicate the user wants to “open” the current selection. Note how the first action of the subclass is to invoke the superclass method to preserve its behavior.

org.eclipse.jface.viewers.StructuredViewer


protected void hookControl(Control control) {
super.hookControl(control);
OpenStrategy handler = new OpenStrategy(control);
...
}



Image When extending methods to destruct objects, such as to unregister hooks or free resources at the end of an object’s life cycle, it is important to call the superclass’sImage 1.1 method at the end. Otherwise, the added code would work on a partially invalid object. Examples can be found in the dispose() methods of the Eclipse editor hierarchy—forImage 12.2.1.2 instance, below class TextEditor. It is useful here to think in terms of parentheses: When adding structure, you invoke super first; when removing structure, you invoke super last—the super calls bracket your own code. Typical examples are given in the EditParts from the Graphical Editing Framework, in methods activate() and deactivate().Image 214


1.4.12 Factory Methods

We claimed in Chapter 1 that classes are second-class citizens that are conceptually less important than objects. One reason is their lack of flexibility. While you can pass objects around at runtime, once you write new Label(), that’s it—you get a plain, simple text label for your UI.Image 7.1There is no chance of creating a more sophisticated display with style markups under different circumstances. Fortunately, this universal problem also has a universally applicable solution:


Pattern: Factory Method

Delegate the choice of a concrete class to be instantiated to subclasses, by introducing a method that subclasses can override.

1. Think about a common interface that all products must implement and make it the return type.

2. Think about the information that possible concrete products may require and pass it as parameters. Note that the object’s fields are also accessible.

3. If a default implementation is not sensible, make the method abstract.


Because factory methods effectively postpone the choice of the class toImage 1.4.1 runtime, analogously to virtual method dispatch, they are also calledImage 1.4.9 virtual constructors. Usually, FACTORY METHODs are called from TEMPLATE METHODs: they are part of a larger algorithm where at one point an object must be created to proceed.

Examples can be found throughout any larger framework. However, they usually mix object creation with initialization. A typical case is found inImage 214 the Graphical Editing Framework, where tools, such as for creating new drawing elements or connections between them, send a requestto the element currently under the mouse. The type of request naturally depends on the kind of tool, which is solved by a FACTORY METHOD:

org.eclipse.gef.tools.TargetingTool


protected Request createTargetRequest()


Note that factory methods are a rather heavyweight tool: Whenever clients want to adapt the behavior, they must derive a new class and override the method. That’s a lot of work and boilerplate code. If only a single instance will be created anyway, it is more flexible to let clients pass in thatImage 1.3.4 instance as a configuration parameter. Especially when you are about to introduce several factory methods, stop to think twice: Clients must create a new subclass for each useful combination of products.


Prefer parameterization by objects to factory methods when possible.


Of course, factory methods can create several instances over the object’s lifetime and can access the entire context of the host object’s fields. So in the end you must make a design decision:


Prefer factory methods if the product is tightly integrated with its context.


Between these two opposite poles, there is a third solution that sometimes balances the forces: Instead of integrating the factory method tightly, you can pass an object containing the factory method. This construction then resembles STRATEGY, if you say that creating an object is just a particularImage 1.3.4 kind of “algorithm.”

A typical example is the JFace SourceViewer, which lies at the core of most of Eclipse’s text editors. On the one hand, that class is fairly complex, so it is not advisable to force developers to subclass it for configuration, because they would be exposed to that complexity. On the other hand, the class needs to be highly configurable by tightly interacting objects. The solution is to have many configuration fields, but to fill them through an ABSTRACT FACTORY, an object that creates objects on demand. The relevant method (greatly abbreviated) then asks the configuration object to create all the features that you also recognize as a user (from the AutoComplete, Quick-Fix, and Format tools):

org.eclipse.jface.text.source.SourceViewer


public void configure(SourceViewerConfiguration configuration) {
fContentAssistant = configuration.getContentAssistant(this);
fQuickAssistAssistant =
configuration.getQuickAssistAssistant(this);
fContentFormatter = configuration.getContentFormatter(this);
...
}


Note that each of the creation methods gets this as a parameter. For instance, the content assistant can be linked to the viewer at creation time:

org.eclipse.jface.text.source.SourceViewerConfiguration


public IContentAssistant
getContentAssistant(ISourceViewer sourceViewer)


The pattern behind this code is summarized here:Image 100


Pattern: Abstract Factory

You want to enable families of logically related objects to be chosen or exchanged at runtime. Then hide the details of how the objects are created behind an interface.

1. Decide which interfaces the products need to implement to fit into the intended context of use.

2. Decide which information from the context should be available to the products—that is, how they can interact with their context.

3. Define an interface containing one method for each kind of product you require. Steps 1 and 2 give the return and parameter types.

As with FACTORY METHOD, it simplifies the life of clients if the interface is given as an abstract class and some products have useful defaults.


Abstract factories have the drawback of fixing the kinds of products,Image 12.3 since each product needs a separate method—abstract factories are not extensible. Extensible abstract factories are obtained by introducing moreImage 214 comprehensive categories of products. For instance, the Graphical Editing Framework uses edit parts to access application data and display it on the screen. For each data item, GEF creates a new edit part. It is very liberal about which edit parts are allowed and does not make too many assumptions about them. Therefore, the abstract factory has a single method. It is extensible, because concrete factories can introduce new kinds of edit parts, one for each kind of appData.

org.eclipse.gef.EditPartFactory


EditPart createEditPart(EditPart context, Object appData);


Abstract factories also have the effect of hiding the classes of the concrete products completely: Clients of the factory can access the products only through the interfaces given in the creation methods. For instance, the W3C has specified Java language bindings for the XML document object model (DOM) as a set of interfaces. The concrete implementation is hidden completely. At the entry point, the library searches for any implementation available in the system; from there, you create documents, their elements, and other things.

xmldom.ReadXML.readXMLDocument


DocumentBuilderFactory builderFactory = DocumentBuilderFactory
.newInstance();


A classic example is the Toolkit of the abstract window toolkit (AWT) introduced in early Java versions. A Toolkit has abstract methods for creating concrete native widgets on the screen, one for each kind of widget supported. To port the AWT to a different operating system, it is sufficient to implement a new Toolkit.

Specializing one step further, abstract factories that intend to hide theImage 1.3.8 Image 235 implementation are often SINGLETONs. For instance, the Eclipse Modeling Framework (by default) hides the implementation classes behind a factory, and creates one instance in a static field eINSTANCE. The same goal can be attained with a different implementation, by making the factory methods themselves static. This choice is applied frequently in the Eclipse platform to hide the implementation of subsystems. For instance, JavaUI provides factory methods for common dialogs related to Java editing. JavaCore can create handles on Java source or class files, Java projects, and so on.Image 3.2.7 Both classes hide the actual implementation classes behind interfaces. SinceImage 1.7.2 clients are shielded from subsystems, these factories can also be considered instances of FACADE.

The size of this section on factory methods and the breadth of their applications reflects their practical relevance. At the end, however, you may be left with the impression that you should be using more factory methods anyway, since they seem to offer so many advantages. However:


Decide to use factory methods only after careful consideration.


Coming back to the initial example, when you write new Label(), you know what you get: a plain, simple, easy-to-use text on the screen. If you delegate the creation, you may still get a “label-like thing,” but you know less about its behavior, so you may be less able to depend on its behavior. Debugging then involves understanding which object has been created and why in the first place. Remember to start with “the simplest thing thatImage 28 could possibly work.” You can always use Extract Method and then Push Down or Extract class to introduce factory methods later on.

1.4.13 Identity, Equality, and Hashing

Objects in Java, and other object-oriented languages, are handled by reference. In other words, variables, parameters, fields, and arrays always contain pointers or references to objects, never the objects themselves. Passing or storing an object means passing or storing a pointer, a really cheap operation. Testing a == b with objects a and b tests whether they are identical, the very same object (i.e., whether a and b are identical as pointers).

Since objects are always handled by reference, they have reference semantics. In the following code, s and r refer to the same object. The call r.translate() modifies that object and the modification is visible through s. The fact that r and s refer to the same object is also calledaliasing of the references.

core.JavaObjectsTest.referenceSemantics


Rectangle r = new Rectangle(100, 200, 40, 50);
Rectangle s = r;
r.translate(20, 30);
assertTrue(s.x == 120 && s.y == 230);


The meaning of an object usually depends on its identity, since it oftenImage 1.1 represents some entity outside of the software world. However, some objectsImage 1.8.4 are conceptually values: One Integer is as good as another Integer as long as they store the same int value; one Stringis as good as another String as long as both contain the same characters. Comparing such value objects by == does not make sense. In particular, there is a common mistake among Java novices:


Do not compare Strings by ==, with the sole exception of constants.


Value objects by convention declare a method equals() that determines whether two objects represent the same value. That method is introduced in Object, where by default it just checks identity by ==. The method is used throughout the library. For instance, collections like Listuse it to find elements, such as in the methods contains() and indexOf(). The requirement on the method’s behavior there is that it is indeed a proper “equality” check (mathematically speaking, an equivalence relation):

1. An object is always equal to itself: a.equals(a). Mathematicians call this property reflexivity, and so does the JavaDoc.

2. An object is never equal to null: a.equals(null) yields false.

3. The argument order does not matter: a.equals(b) is always the same as b.equals(a) (provided neither a nor b is null, of course). Technically, this is called symmetry.

4. Groups of equal objects can be identified: if a.equals(b) and b.equals(c), then a.equals(c). This property is called transitivity.

Obtaining these properties may be, in fact, a bit intricate, as the following implementation for an object with a single int field val shows. Lines 2–3 take care of the special case 2. Lines 4–5 play a double role. First, they ensure that the downcast in line 6 will succeed. Second, they ensure property 3; simply using other instanceof IntVal in line 4 would break symmetry in case other is an instance of a subclass that itself overrides equals(). Property 4 then follows, because the checks in lines 4 and 7 are transitive if both are successful.

equality.IntVal.equals


1 public boolean equals(Object obj) {
2 if (obj == null)
3 return false;
4 if (getClass() != obj.getClass())
5 return false;
6 IntVal other = (IntVal) obj;
7 if (val != other.val)
8 return false;
9 return true;
10 }



Image Just in case you care to double-check: Integer.equals() in the library does use instanceof, but that class is final, so that no subclasses can break symmetry.


Fortunately, Eclipse has a tool that takes care of these details for us. In fact, it has generated the preceding example code.


Tool: Generating equals and hashCode

Use Source/Generate hashCode and equals to obtain a correct implementation that checks selected fields for equality.


The second method generated here, hashCode(), is also declared in Object. It is employed in HashMap, HashSet, and their variants, for hash-basedImage 72 indexing in large collections. Roughly speaking, they look at a few bits of an object’s hash code first and then search linearly only through the (relatively few) objects that happen to have the same bits. For this scheme to work out, hashCode() should exhibit the following 'margin-top:4.0pt;margin-right:0cm;margin-bottom:4.0pt; margin-left:40.0pt;text-indent:-12.0pt;line-height:normal'>1. The result is compatible with equals(); that is, a.equals(b) implies a.hashCode()==b.hashCode(), since otherwise the equal objects will not be found by the above search.

2. The method returns the same value, as long as the object’s fields do not change.

3. The hash code should be pseudo-random, to minimize the chances that non-equal objects have the same hash code.

The default implementation Object.hashCode() must therefore match the default implementation of equals, which checks object identity ==. The corresponding hash code is computed by System.identityHashCode(), which is usually based on the object’s address.


Image Conscientious readers will note that there is a possible interaction with efficient moving garbage collectors: Moving an object to a new address must not changeImage 1.1 Image 133 its hash code because of property 2. The solution is to tag the (relatively few) objectsImage 19(§3.3) whose hash code has been computed and that have actually been moved, and to store the original hash code explicitly in this case.


You may ask at this point why it is necessary to know these requirements at all, if Eclipse will generate the methods anyway. The answer is simple: As a developer, you are still responsible for the code, you will work with the code, and you may at some point need to understand and maintain equals and hashMap implementations that have not been generated by Eclipse. And even if Eclipse has generated them, you are still responsible for regenerating them if your class acquires new fields.

1.4.14 Refused Bequest

In principle, subclasses must offer all services that their superclass andImage 3.1.1 interfaces specify; after all, the client cannot know whether a method hasImage 1.4.1 been overridden. Every once in a while, however, a subclass simply cannot provide the services efficiently—for instance, because it uses a representation optimized for space rather than speed. Then it inherits a method that it does not want, a refused bequest. In such cases, clients are responsible for working around that limitation. For instance, they might have opted for a space-efficient implementation precisely because they do not require the more complex services anyway.


Avoid refusing bequests; if you must, be loud and throw an exception.


One usually throws an UnsupportedOperationException to indicate a refused bequest. It signals that the client has used the full generality of the object, even though the client should have been aware of its limitations. If you search in Eclipse for this type with Match Locations set to Instance creation, you will be surprised at the number of hits (roughly 1500 in Eclipse 4.3).

For instance, a ListLineTracker, according to its documentation, is a read-only implementation of the ILineTracker interface. It determines the lines once, when the text is set. Correspondingly, notifications that the text has changed are refused:

org.eclipse.jface.text.ListLineTracker


public final void replace(int position, int length, String text)
throws BadLocationException {
throw new UnsupportedOperationException();
}


There is one context in which refused bequests are common and acceptable:Image 5.3.2.1 Mock objects for testing purposes must be lean and provide only as much functionality as required in the test scenarios. Otherwise, we might as well wait for the real implementation to be ready before commencing testing.

1.5 Exceptions

Exceptions are not an object-oriented feature themselves. We discuss them briefly nevertheless, because they heavily influence the style of collaborationImage 1.5.7 between objects. Independently of whether we choose to declare them explicitly in methods’ interfaces or use unchecked exceptions throughout, callers must be aware of possible exceptions and must be prepared to react correspondingly. At the same time, robustness and proper error handling are major requirements for professional code.

1.5.1 Basic Usage

Java’s exception mechanism is really straightforward: throw stops normal execution; code in the block is skipped, methods are terminated, and so on until a try/catch block is found. This behavior is called abrupt termination.Image 111 If a catch clause matches the thrown exception, the exception is caught and normal execution resumes in that exception handler. Any finally blocks encountered on the way are guaranteed to be executed as well.

From a broad perspective, the conceptual contribution of exceptions is this:


Exceptions indicate circumstances that prevent code from achieving its purpose.


The real benefit of exceptions is that return statements are also skipped, soImage 6.3 that methods are freed from the obligation to compute the expected return value, or more generally to achieve the effect that the caller expected. At the same time, the caller is notified of this fact and can try to recover from the failure.

For instance, a ProjectDescriptionReader reads the .project files from Eclipse projects. Clients simply call the method read() shown next. Of course, a number of things can go wrong on the way: The file may not exist, the user may lack access rights, the content may not be valid XML, and so on. When anything happens that keeps the method from fulfilling its purpose, it will just throw an IOException.

org.eclipse.core.internal.resources.ProjectDescriptionReader


public ProjectDescription read(IPath location) throws IOException {
...
file = new BufferedInputStream(
new FileInputStream(location.toFile()));
return read(new InputSource(file));
...
}


From the client’s perspective, the exception shows that the project description could not be read. What is it supposed to do? There are really several routes open:

• It can recover from the error by catching the exception and, for instance, looking elsewhere for the missing file.

• It can pass the exception on to its own caller by declaring throws IOException as well.

• It can throw a different exception to its own callers to indicate that it cannot achieve its own purpose.

The second and third options deserve special attention. The client will quiteImage 161 often have a purpose that does not agree with the exception that occurred.


Choose exceptions to match the method’s purpose.


For instance, the ProjectDescriptionReader is a helper to Workspace, which implements the functionality of the overall Eclipse workspace you are familiar with. Clients of Workspace expect higher-level error messages, about resources in the workspace. Therefore, the following method wraps the low-level message into a higher-level one. Note how the new exception includes the original one, so that the details do not get lost.

org.eclipse.core.internal.resources.Workspace


public IProjectDescription loadProjectDescription(IPath path)
throws CoreException {
try {
return new ProjectDescriptionReader().read(path);
} catch (IOException ex) {
IStatus status = new Status(/* more information */ex);
throw new ResourceException(status);
}
}


This pattern is encountered so frequently that all exception types in the library offer a cause argument, by which you can attach arbitrary exceptions.


Use the cause argument to exceptions to preserve error details while achieving the right level of abstraction for the message.


Image 44(Item 45)Similarly, you should strive to capture in the exception’s message enough information for maintainers to figure out what went wrong when they see the stack dump in a log file.

To summarize, you should use exceptions to indicate that a method was not able to deliver the promised result, and to explain precisely what went wrong and why. Inside methods, abrupt termination then indicates that pieces of code have failed to achieve their purpose. Exception handlers, in the form of catch clauses, then resume normal execution. However:


Catch an exception only if you can recover from the failure.


Exceptions indicate that something in the planned course of events went wrong. When you catch an exception, you must be able to achieve the expected result of your own code regardless of that failure—for instance, by trying a different strategy or by retrying the same strategy.


Never catch and ignore an exception out of laziness or for your convenience.



Image For a compiler error Uncaught exception, Quick-Fix offers Surround with try/catch or Add throws declaration. The “surround” option is tempting, because the “throws” might require changes elsewhere. Nevertheless, “throws” should be your default option, unless you can really handle the error. A third option is to catch the exception and wrapImage 1.5.7 it with an unchecked exception.


If you decide to ignore an exception, document the decision. For instance, the method close() in I/O streams formally throws IOException, but there is really nothing you can do about it: Either the resource is freed, or you can’t retry anyway. The Eclipse code base even contains a utility method for this situation:

org.eclipse.core.internal.utils.FileUtil


public static void safeClose(Closeable stream) {
try {
if (stream != null)
stream.close();
} catch (IOException e) {
// ignore
}
}



Very often, an error message to the user is the only recovery strategy.


Most operations and computations are triggered directly or indirectly by the user, usually by pushing some button or selecting some menu entry inImage 7 the UI. The user then expects the operation to be carried out successfully, unless notified differently. Depending on the operation, the user will not appreciate it if your application tries various strategies, since that gives the user a sense of losing control. When displaying an exception, keep in mind that the user, like each method, has a particular level of abstraction: He or she is not interested in the implementation, but rather in why the original command failed.

Finally, there is finally.


Use finally to free resources and leave objects in consistent states.


Fields and variables tend to be in a somewhat “messy” state in the middleImage 1.5.6 Image 4.1 of operations: Some assignments have been performed, while others have been aborted by the occurrence of an exception. The finally clause of a try block gives you the opportunity to free resources—for example, to close files or network connections, that may still be in use, make sure that locksImage 148 are released, and perform similar cleanup operations.

1.5.2 Exceptions and the System Boundary

A premier source of exceptions is the system boundary. There, the inputImage 1.8.7 Image 4.6 received from the user or from other systems, the data read from the harddisk, and several other things are frequently faulty. Similarly, the network connection you are about to read from may turn out to be closed, and the directory you are writing to may not be accessible to the user. In short:


Be paranoid whenever you reach beyond your own software system.


Being paranoid here means two things. First, you should check any data you receive into your system and check that any operation you perform has actually succeeded (unless that operation already throws an exception). Second, you should abort an ongoing operation by an exception rather than keep going despite previous faults, on a shaky and insecure basis. Suppose, for instance, that you need a particular directory to create files in. The following method checks for the different failure conditions and returns normally only if the directory exists in the end. (mkdirs() doesnot throw an exception.)

exceptions.CreateDirectorySafely


public void ensureDirectoryExists(String absolutePath)
throws IOException {
File dir = new File(absolutePath);
if (!dir.exists() && !dir.mkdirs())
throw new IOException("Failed to create "+absolutePath);
if (!dir.isDirectory())
throw new IOException("File already exists: "+absolutePath);
}


Further caution is in order when applying the guideline to recover by returning an error message. Users will start to distrust your system if they see long stack traces they do not understand. These traces are for the log files. Worse still, if the operation was triggered from an external system, it may actually be dangerous to return the stack trace, as it may potentially reveal internal structure that attackers can exploit—for instance, because they know of the weakness in one of the libraries your system employs.

1.5.3 Exceptions to Clarify the Program Logic

Code is readable when it reflects the solution strategy clearly, so that human readers can align the technical details with their general understanding. The solution strategy usually assumes that intermediate steps work out correctly. It captures the salient points of the plan and defers the details of error handling to a later point, even though they are essential for achieving robustness.


Use exceptions to separate the control flow of normal and failed executions.


Image 31Exceptions enable the developer to transfer this mode of presentation toImage 1.4.5 the code: to state the salient points in a sequence of processing steps and to keep error handling implicit, until the catch clause adds the required logic for the unexpected cases in a single place.


Distinguish “normal” and “failed” executions from the client’s perspective.


When defining a method’s interface, one has to think twice whether a particularImage 6.1.3 result constitutes a failure. For instance, HashMap.get() returns null if the given key is not found. For most clients, this is just right, since they fully expect that some keys are not present. TheSymbolTable of aImage 2 compiler, which stores the definitions of names in the current context, is quite a different matter: When get() does not find a binding for a variable, this is in fact a failure, because normal compilation will not be able to continue without that information. It is therefore better to throw an exception within the method. Note that this choice also follows the previous guideline about “normal” and “failed” executions.

1.5.4 Exceptions for Checking Expectations

When coding, you make all sorts of assumptions. For example, when calling a method in obj.m(), you assume obj is not null; when accessing a[i], you assume a is not null and i is between 0 and a.length (exclusive); when calling Math.sqrt(x), you assume that x is not negative, because that is what sqrt() demands in its documentation. You might consider testing these assumptions to make sure there will be no problems. Here is some sample code that initializes the first part of an array a up to index end (exclusive):

exceptions.CheckingAssumptions.initUpto


if (a == null) throw new IllegalArgumentException("a is null");
for (int i=0; i!=end; i++) {
if (i < 0 || i >= a.length)
throw new IllegalArgumentException("index");
a[i] = data;
}


Although in general this checking will be cumbersome, there are some situations in which you’d better make sure your reasoning is correct.


Check before irrevocable operations with possibly dramatic consequences.


For examples, look into Eclipse’s abstraction over the file system called resources, such as in class File (in package org.eclipse.core.internal.resources). The workspace is a complex data structure on which the entire Eclipse platform depends. If anything goes wrong there, the user’s work may be lost. Similarly, you can double-check before performing externalImage 1.8.7 operations, such as moving a robot arm.


Check when in doubt about the caller.


Image 1.5.2 Image 1.8.7A situation analogous to the system boundary occurs at the beginning of methods. You will document which parameter values you allow and perhaps under which circumstances the method can be called, but you cannot be sure that the caller has read, understood, and obeyed the documentation. When conditions get complex, this is a good reason to double-check.

Image 7To give an example for the last two guidelines, SWT builds on a native implementation of user interface elements. Since that is written in C, accessing it incorrectly may bring the entire JVM crashing down, and the running Eclipse application with it. Even such an apparently trivial thing as setting a label’s text therefore checks that the assumptions, such as the label not being destroyed and the current thread being the distinguishedImage 7.10 UI thread, actually hold before proceeding to the native code.

org.eclipse.swt.widgets.Label


public void setText(String string) {
checkWidget();
...
OS.gtk_label_set_text_with_mnemonic(labelHandle, buffer);
...
}


A typical idiom is to throw IllegalArgumentException, such as in Eclipse text editors shown next. Here, the justification for checking lies in the fact that it may depend on the concrete class of the editor whether the passed mode is allowed, so that there are complex interdependencies that the caller may not be aware of.

org.eclipse.ui.texteditor.AbstractTextEditor


public void setInsertMode(InsertMode newMode) {
List legalModes = getLegalInsertModes();
if (!legalModes.contains(newMode))
throw new IllegalArgumentException();
...
}



When in doubt about the program logic, use assert or Assert.isTrue.


Lengthy case distinctions and complex control flows possibly involving exceptions may leave you in doubt about your own reasoning: Under which conditions and after which operations can a specific statement actually be reached? Usually there are one or two crucial deductions that you make from those circumstances. The best option is to simplify the control flow, soImage 4.1 that your doubts dissolve because the deductions are obviously correct. The second best option is to write an Assert.isTrue (or assert) and state the outcome of your reasoning process: During the entire testing phase, your results will be reconfirmed on each run of the code.4 What is more, the maintenance developer who comes after you will find your central insights well documented and won’t spend time on reestablishing or refuting your reasoning.

4. When using assert, you must start the JVM with the enable assertions flag -ea. Look at the launch configuration of your tests or the default arguments of the JVM in the Eclipse preferences.


Check the else branch after long or complex if-then-else chains.


This is a special form of the previous strategy: After a sequence of cases tested by if, the final else must handle the remaining situations. At this point, you know that all previous if tests have returned false. Unfortunately, humans are very error-prone in deducing from many things that do not hold the things that do hold. Who has never made mistakes in applying de Morgan’s laws? Better introduce an assertion.


Image As a reminder, de Morgan’s laws state ¬(PQ) = ¬P¬Q and ¬(PQ) = ¬P¬Q. The first one, read backward, captures the situation of nested ifs.



Check when you rely on some place not to be reached.


In several situations, you know that a specific place in the code can never be reached: You have handled all allowed cases through if or switch statements, or you have caught an exception that will never be thrown anyway. In this case, you would be justified in leaving the else branch, the default case, or the catch block, respectively, empty. It is better, however, to be careful: Your reasoning may be faulty or a new case may crop up in the future. Just throw some RuntimeException with a message like “This can’tImage 142(Ch.27) happen.” It will save you or a fellow team member much debugging effort later on. As a special case:


Don’t leave out the default in switch. Throw an exception.


In all of these instances, professional developers would check whether their reasoning, or the reasoning of some caller, is correct. Novices, in contrast, tend to check too much and in too many places. This section therefore concludes with some converse advice:


In general, do not check expectations and assumptions.


The reason is simple: Any code that you write for checking, you also have to debug, maintain, document, and manage throughout its lifetime. In theImage 6.1 Image 4.5 introductory example in this subsection, more than half the code went into checking assumptions. Checking has completely cluttered the very simple logic itself. Besides, it also costs runtime. And the code does not deliver any benefit in return; it just confirms what you already knew anyway.

1.5.5 Exceptions to Signal Incompleteness

Exceptions are also a wonderful tool for prototyping, exploratory programming,Image 5 and test-driven development. With exceptions, you first handle only the cases that you need for now, while you are well aware that some other cases need to be handled, perhaps with more knowledge and further specifications, later on. The central rule for such a strategy is this:


Never continue silently when you know something is not (yet) handled correctly.


Perhaps your if statement is thought through for the then branch, which you need for the current test. In that case, just throw an Unsupported OperationException or IllegalArgumentException in the else branch, as a reminder to yourself that this part of the code is not finished. As soon as you write the first test case that relies on the missing functionality, you will get the exception and can fill in the right code there: The test will drive you to code the functionality, as intended.

Exceptions thrown to signal incompleteness are always derived from RuntimeException, so that they do not have to be declared in method interfaces. After all, they won’t be thrown in the production version.

1.5.6 Exception Safety

Unfortunately, exceptions can easily get out of hand when a longer block of code can throw them in different locations. Exception safety denotes the property that no severe damage is done regardless of where an exception occurs. Damage can have several meanings here, up to physical damage done by robot arms that fail to stop and rockets that are blown up in mid-air for safety reasons.5

5. This has actually happened, as is well known: On the maiden flight of the Ariane 5, a floating-point measurement of the vehicle’s horizontal velocity was unexpectedly high, so it was out of range in a conversion to an integer. This triggered an exception that caused the navigation system to stop itself due to an unexpected failure. As a consequence, the on-board system could no longer control the flight and initiated the rocket’s self-destruction [87, §2.1].

Image 6.3In most cases, consequences are not so dramatic, since they are confined to the software itself. Here, damage usually means broken object structures and leaked resources. Consider a simple list of sensor readings that are arranged in a linked list (Fig. 1.5). A new reading rn is to be inserted after a given node.

Image

Figure 1.5 Example of a Destroyed Invariant

The code seems simple enough; it just implements the pointer diagram in left-to-right fashion:

exceptions.SensorReadingsList.insertCurrentReadingWrong


1 Node newNode = new Node();
2 Node tmp = after.next;
3 after.next = newNode;
4 newNode.value = readSensorCurrentValue();
5 newNode.next = tmp;


Unfortunately, it is not exception-safe: When the reading of the sensor in line 4 throws an exception, the list structure is destroyed, because the partially initialized node newNode is found in its middle. The entire list is rendered unusable and the application will likely have to be shut down, even if the next reading succeeds.

To localize damage, one has to keep the list structure intact regardless of exceptions. The following code does just this: It allocates the new node, but reads the sensor before even touching the list. If line 2 throws an exception, there is no problem at all; the caller will be informed that this one reading failed, but it can decide to retry the operation at a later point.

exceptions.SensorReadingsList.insertCurrentReadingRight


1 Node newNode = new Node();
2 newNode.value = readSensorCurrentValue();
3 newNode.next = after.next;
4 after.next = newNode;


A further major kind of damage is that of leaked resources. Resources, such as open files, network connections, or system-level images and icons, are not cleaned up by the garbage collector, but must be released explicitly. Leaking resources means that the software allocates them and then loses control of them. Over time, this fills up memory and makes the application crash. The finally block is a good point to take care of that concern, since it is executed regardless of how the try block is left: by exception, with or without a catch, or even by return, break, orcontinue.

A typical example is found in ImageIO, from the JDK. Its method read, shown here, is supposed to read an image from the disk. It opens an input stream (line 2). If that throws an exception, nothing is lost, since the stream is not actually opened. In contrast, if read in line 6 throws an exception, then the stream would be leaked. The finally takes care of this concern. (Note that istream!=null at this point by line 2.)

javax.imageio.ImageIO


1 public static BufferedImage read(URL input) throws IOException {
2 InputStream istream = input.openStream();
3 ImageInputStream stream = createImageInputStream(istream);
4 BufferedImage bi;
5 try {
6 bi = read(stream);
7 } finally {
8 istream.close();
9 }
10 return bi;
11 }


The call to createImageInputStream in line 3 is not protected by the finally clause—it would seem that the developer was very sure that this method will never throw an exception. In fact, this assumption is not justified at all, as a quick look at the method’s code reveals: It creates a cache file and that step may, of course, fail. The library code is not exception-safe at this point.

To be on the safe side, you can adopt the following coding idiom, whichImage 148 derives from a corresponding guideline for locks:

exceptions.AllocReleaseResourceIdiom.demoOperation


resource = allocateResource();
try {
// work with resource
} finally {
resource.release();
}


Image 111(§14.20.3)Java 7 recognizes the importance of this idiom and provides language support in the try-with-resources construct: Objects initialized in parentheses after try are guaranteed to be automatically closed before leaving the try block. Initializations of several objects are separated by semicolons; all variables declared here must have subtypes of AutoCloseable.

exceptions.AllocReleaseResourceIdiom.tryWithResources


File file = File.createTempFile("output", "tmp");
try (FileOutputStream out = new FileOutputStream(file)) {
...
}


1.5.7 Checked Versus Unchecked Exceptions

Image 111(§11.1.1)Java distinguishes three kinds of exceptions. First, there are errors that signal fundamental problems such as OutOfMemoryError or ClassFormat Error. They are neither thrown nor caught by applications, and are derived from the special class Error. You won’t meet them in daily practice. Second, subclasses of Exception denote failures from which applications may try to recover. These are called checked exceptions, because the compiler tracks where they are thrown and requires a method to either catch them or declare them in a throws clause. Third, a special case of Exceptions includes subclasses of RuntimeException, which are called unchecked exceptions, because they are not tracked by the compiler.

In the Java community, a long and sometimes heated debate has arisen about whether checked or unchecked exceptions should be preferred. SomeImage 172(Ch.7) claim that the debate is over and that the “unchecked” party came out victorious. However, many professionals do use checked exceptions, and with very good reasons. As usual in software development, there is no simple answer to the question of which is best.

We already know the arguments for checked exceptions: They clarifyImage 6.3 Image 1.5.1 the behavior of a method, because they also make explicit its limitations and possible failures. Clients that are aware of these limitations can take precautions to guarantee exception safety. If the system uses unchecked exceptionsImage 1.5.6 throughout, code must be really paranoid, because exceptions may be lurking in any method call, or else developers must be really disciplined, and document all thrown exceptions. Furthermore, coding idioms can beImage 240 introduced, but must then be obeyed meticulously. All of those tasks areImage 161 greatly simplified when one knows which operations may actually fail.

The argument for unchecked exceptions looks beyond single methods, toImage 172(Ch.7) strategic planning of entire applications. Very often, their structure contains situations like that shown in Fig. 1.6. In this example, the application uses some library or framework, such as SWT or SAX parsers for XML. InImage 7 each case, the library or framework calls back application code when itImage 7.3.2 encounters specific situations. The problem with checked exceptions at this point is that the framework cannot be modified, so that the callbacks cannot throw checked exceptions at all. In effect, they must not fail, so that they cannot, for instance, reach beyond the system boundary. Similar argumentsImage 1.5.2 also apply to mechanisms inducing flexibility into systems themselves.Image 12.2

Image

Figure 1.6 Motivation for Unchecked Exceptions

In software engineering, being dogmatic is usually the worst option. Before deciding for one or the other side, let us first analyze the usage in state-of-the-art code. This is useful to understand existing software, if nothing else. The general strategy found there can be summarized asImage44(Item 58) follows:


Use checked exceptions for expected failures.


If exceptions indicate that a method could not fulfill its purpose, then expected failures are those that the software cannot be held responsible for—forImage 1.5.2 instance, those occurring during I/O.


Use unchecked exceptions for failures that should not occur.


Failures that should not occur usually indicate an error in the program logic. The subclasses of RuntimeException, which are just the unchecked exceptions, bear witness to that usage: NullPointerExceptions are thrown when the developer thought they had a proper object reference; ClassCast Exceptions, when they thought they had a special subtype; Illegal ArgumentExceptions when the caller disobeyed the rules stated for aImage 1.5.4 method; and so on. At crucial points, code will also actively check its expectations.

Since there are good reasons for both styles, let us be pragmatic—we can often convert one style into the other. The first direction is simple: You can always catch a checked exception and wrap it in an unchecked exception. The following snippet from SWT exchanges an IOExceptionfor an SWTError.

org.eclipse.swt.graphics.ImageLoader


try {
stream = Compatibility.newFileInputStream(filename);
return load(stream);
} catch (IOException e) {
SWT.error(SWT.ERROR_IO, e);
}


This wrapping idiom is also useful if according to the program logic, the checked exception should not have been thrown in the first place.


If an exception should not occur logically, catch it and wrap it.


The other direction requires some support from the framework. For instance, a SAX parser scans an XML document and calls back methods in a ContentHandler for each tag it encounters. Those methods are allowed to throw a generic SAXException, which applications can use to wrap their own specific failures. The invoking part on the left of Fig. 1.6 then unwraps the original using getCause().

org.xml.sax.ContentHandler


public void startElement(String uri, String localName, String qName,
Attributes atts) throws SAXException;
public void endElement(String uri, String localName, String qName)
throws SAXException;


1.6 Constructors

Before an object can be used, it must be initialized. In other words, data structures and helper objects must be created and connected, primitive indices have to be set up, and so on. The object must be in working order at the time the first public method gets called. Constructors are special methods that perform this initialization and are invoked within new expressions, right after the memory for the new instance has been allocated. Conceptually, they start on a raw memory area and leave a proper object. This view is also reflected in the byte code. For an example, run javap -c on class CtorCall from the online supplement.


The constructor leaves a new object in ready-to-use condition.


In a different formulation, one can say that the object is consistent after the constructor finishes: Everything within the object is just as it should be. InImage 4.1 yet another view, the constructor must establish the object’s invariants.

1.6.1 Initializing Objects

Allocation of an object presets all fields to their default values, such as null for references, 0 for int, and false for boolean variables. Basically, the object’s memory area gets overwritten with 0 bytes to ensure that no stale pointers or data is left that would confuse the JVM’s machinery. Beyond that, the programmer is responsible for proper initialization.

Let us first consider a typical case with data structures. The doubly linked list in LinkedList uses a sentinel node—that is, a node that does notImage 72 contain data, but enables a uniform treatment without case distinctions for the empty list. The initialization code sets up precisely this data structure.

java.util.LinkedList


1 private transient Entry<E> header = new Entry<E>(null, null, null);
2 private transient int size = 0;
3 public LinkedList() {
4 header.next = header.previous = header;
5 }


Lines 1–2 initialize the fields using variable initializers; the compiler prepends these to the constructor code. Then, the sentinel node is set up to be the only node in the list. For the details of this inlining, you can run javap -c on class DemoInitializers from the sample code.

The preceding constructor is called a default constructor, which does not have any arguments. You can create a default constructor by placing the cursor into the class body, outside any method, and invoking Auto-Complete.

Not all objects require data structures. Many information holders basicallyImage 1.3.3 store properties, which can be initialized by values provided by their clients.


Tool: Create Constructor for Fields

To initialize an object’s properties with given values at creation time, use Source/Generator Constructor Using Fields.



Image Beware of breaking encapsulation by enabling the client to pass unsuitable values orImage 1.3.3 objects for internal fields. As a rule of thumb, if a field is not accessible otherwise, its initial value should not be provided by the client.


Image 1.4.5Constructors can also be created by wishful thinking, similar to Create Method from Call: You just write down the constructor call passing all desired information, and create the constructor by Quick-Fix. Then, you go through the new constructor’s parameters using the following tool:


Tool: Create and Initialize Field

Place the cursor on a constructor’s parameter. Open Quick-Fix (Ctrl-1) and choose Assign parameter to new field. The tool also works for parameters of other methods and also offers a variant Assign parameter to field ... that initializes an existing field.



Make constructors run quickly. Make long-running initializations lazy.


Image 1.1A fundamental assumption of object-oriented programming is that objects are small and cheap. Clients expect to create an object quickly, so the constructor should not open network connections, read files, or performImage 1.6.2 other tasks. Clients do, however, accept that methods can run longer. One alternative to full initialization in the constructor is to defer initializationImage 1.3.8 to a life-cycle method called. We will discuss this approach shortly. Another option is lazy initialization, which we have already seen in the SINGLETON pattern. With this technique, the constructor sets up only rudimentary structures and marks those parts of the object that have not yet been initialized—for instance, by setting the fields to null. Typically, one then creates a getter that initializes the structure on the fly:

ctor.LazyInitializationDemo


private DataStructure getHugeData() {
if (hugeData == null) {
hugeData = new DataStructure();
hugeData.fetchFromDisk();
}
return hugeData;
}


At a larger scale, platforms like Eclipse or Netbeans rely on lazy initializationImage 174(§27.8)Image 50 to make the startup process bearable. Plugins, their classes, and data structures are initialized only when the user first accesses their functionality—that is, at a point where the user is prepared to wait for a bit. Also, plugins that never get used are not initialized at all.

1.6.2 Initialization by Life-Cycle Methods

While constructors and initializers work in most cases, it is sometimes necessary to make initialization an explicit step in the object’s life cycle. A frequently encountered case is an object that is constructed by reflection. Extensibility mechanisms like Eclipse’s plugins or Netbeans’ modules takeImage A Image 50 class names from XML documents and instantiate the classes basically as follows, where newInstance requires a public default constructor to succeed. The interface CreateByReflection in the example captures the minimal expectations, similar to the situation with factory methods.Image 1.4.12

ctor.CreateByReflection.main


Class<?> namedClass = Class.forName(className);
CreateByReflection inst =
(CreateByReflection) namedClass.newInstance();


Looking up a proper constructor for specific arguments would greatly complicate matters. However, once the static type is known after the cast, it is a simple matter to invoke methods for further initialization. For instance, views in Eclipse, such as the Package Explorer and Outline, are created byImage 12.3.3.4 reflection and are wired to their context by the init() method afterward:Image 12.3.2

org.eclipse.ui.IViewPart


public void init(IViewSite site) throws PartInitException;


The idiom of a separate initialization method is used very widely, as the following example from the Servlet API shows. A servlet is a Java object that lives in a servlet container and is called back to answer incoming service requests, typically HTTP requests. When the container first instantiates the servlet, it invokes the following method:

javax.servlet.Servlet


public void init(ServletConfig config) throws ServletException;


A further benefit of using life-cycle methods is that they can be defined in an abstract base class once and for all. The passed information is thenImage 3.1.4 available “magically” to all subclasses.

1.6.3 Constructors and Inheritance

Unlike ordinary methods, constructors are not inherited. The reason is very simple: An inherited constructor would initialize only the inherited fields, but would leave the newly introduced fields with their raw default values. This is in general insufficient, and rarely desirable. Instead, initialization best proceeds in stages along the inheritance hierarchy:


Each constructor initializes the fields declared in its own class.


Image 6.4.2This general rule goes together with the insight, to be discussed later, that derived classes must not modify the consistency conditions, or invariants, of their superclass.

To enable this initialization strategy, Java allows one super call at the beginning of a constructor. This call delegates the responsibility for the inherited fields to the superclass constructor.


Tool: Generate Constructor from Superclass

If you wish to “inherit” a constructor, invoke the tool Source/Create Constructor from Superclass (Alt-S C by menu mnemonics).



Image A few restrictions apply in the super call. Since the object is not yet initialized, this cannot be accessed. In particular, the object’s methods cannot yet be calledImage 1.8.8 to compute the arguments to the super call. Also, non-static nested classes cannot be instantiated, because they require a this reference internally.



Image Even though Eclipse usually generates it, a call super() to the superclass’s default constructor is not necessary. It is inserted by the compiler if no superclass constructor is invoked. You can disable the generation of the call in the creation dialog.


There is one pitfall lurking in the interaction of method overriding and the nice strategy of initializing along the levels of the inheritance hierarchy: The super-constructor might invoke a method that is overridden in the subclass—and that can access a part of the object that is not yet initialized at all. In such a situation, of course, a multitude of things can go wrong.

Suppose you design the following (faulty) base class for windows on the screen. The createContents method is supposed to actually set up the displayed elements.

ctor.WindowBase


public WindowBase() {
createContents();
}
protected void createContents() {
...
}


Derived classes overriding that method must be aware that their own constructors have not yet run. The following method actually sees 0 in the special field:

ctor.WindowDerived


private int specialField = 2;
protected void createContents() {
super.createContents();
System.out.println("Current content of specialField: "
+ specialField);
}



Think carefully about the expected runtime behavior when invoking methods in constructors.


These complexities are the reason that some initializations are best deferred until later in the life cycle. For instance, the real class Window does not invoke createContents() within the constructor, but rather when the client uses open().Image 1.4.5

1.6.4 Copying Objects

Some constructors are used for copying objects, especially those of mere information holders and value objects. For instance, the collections frameworkImage 1.8.3 Image 1.8.4 generally provides constructors to initialize a collection from another collection.

java.util.ArrayList


public ArrayList(Collection<? extends E> c)



Image This style of construction is very common in C++, under the designation of copy constructor.Image 239 Image 144 Copy constructors are particularly relevant there because not all objects are handled by reference. Those that are stack-allocated must be copied for initialization and parameter passing. For a class C, the compiler recognizes a constructor with parameter const C& (i.e., a reference to an immutable object) as a copy constructor and invokes it implicitly in these situations.

Similarly, the compiler will recognize conversion constructors, which have a parameter const D& as implicit conversions from D to C, and will apply them according to the overload resolution and conversion rules.


1.6.5 Static Constructor Methods

Constructors always have the same name as the class, so that you cannotImage 1.2.3 Image 1.4.5 use their names to express intentions. If there are several useful ways to initialize an instance, you can use only overloading, which does not captureImage 1.4.2 the differences at all. Imagine different calls to the constructors of URL shown next; to understand what happens, you have to trace the types and resolve the overloading in your head.

java.net.URL


public URL(String spec) throws MalformedURLException
public URL(URL context, String spec) throws MalformedURLException
public URL(String protocol, String host, int port, String file)
throws MalformedURLException
public URL(String protocol, String host, int port, String file)
throws MalformedURLException


Image 44(Item 1)In such circumstances, it can be sensible to make the constructor(s) private or protected and to provide public static constructor methods instead.Image 1.7.2 The methods can also be located in a separate class, to gather entry points to the API in a single spot in the manner of a FACADE. You gain the same benefit as with well-named methods:


Static constructor methods can make the client code more readable.


Image 148A typical example is found in Java’s concurrency library. Executors take on tasks and schedule them for background processing according to configurable heuristics and strategies. Clients can use executors in a simple fashion because static constructor methods capture the most common cases. For example, the client might decide on a fixed number of threads, with the constructor method configuring a generic thread pool accordingly:

java.util.concurrent.Executors


public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}


This example also shows that constructor methods can be smart. Since the constructor itself is private, you can move some of its responsibilities for a “proper” initialization to the static method. This smartness can even include figuring out the correct (sub)class to be instantiated, similar toImage1.4.12 an ABSTRACT FACTORY. For example, MessageDigest.getInstance() takes the name of the desired algorithm and searches through the available implementations at runtime and instantiates a suitable subclass of MesssageDigest.

Two differences from earlier methods performing object constructionImage 1.4.12 must be pointed out. First, in contrast to factory methods, we are now talking about static methods. The intention of substituting the instantiatedImage 1.3.8 class at runtime is not present. Second, in contrast to the SINGLETON pattern, several instances can be created. Static constructor methods are really just that: wrappers around the private constructors.

1.7 Packages

Packages serve two technical purposes in Java. First, they introduce name spaces, so that the same class name can be reused in different libraries without clashes. Second, they introduce a visibility scope: If none of public, private, or protected is specified, then the class or member is visible within the defining package. Incidentally, protected elements are also visible throughout the package, because, like subclasses, the package members are supposed to contribute to the implementation of the protected elements.Image 111(§6.6.2)

The use of packages as namespaces keeps apart the code from different libraries, frameworks, or (sub)projects. It is good practice to choose hierarchical naming schemes, starting with the organization, then the (sub)project. The Organize Imports tool enables you to work effectively with such namespaces.Image 1.2.1 This use is, however, a rather crude structure that does not tell anything further about the classes contained in the packages.


Image As a rule of thumb, don’t reuse class names in different packages, because that makesImage 263(p.92) Image 1.2.3 the clients’ code less readable. When naming classes, think at the application level.


1.7.1 Packages as Components

The use of packages as a naming scope is much more fine-grained and reflects design decisions at the level of objects and classes. Usually, groupsImage 1.1 of objects work closely together to accomplish some task—they form a neighborhood. However, clients that employ this functionality are often notImage 11 aware of the inner object structure behind its accomplishment—the objects form a component that can be reused as a whole, while the constituent objects are not useful outside the given context. In such a situation, only a few objects are offered to the client as public classes; the others are default-visible. The package creates a common, trusted space where the neighborhood can work without interference from the outside.

A typical example is SWT’s Browser. It enables you display web pagesImage 7 within your own Eclipse-based application. Because the functionality is rather complex, the package org.eclipse.swt.browser contains many default-visible helper classes that provide specific aspects of the overall behavior.


Image The default visibility constraint does not offer any protection against malicious users. For instance, if different libraries (i.e., JAR files) contain the same package, these are actually merged at runtime. By just specifying the same name for a package, one can easily gain access to the default-visible members. The projects chapter02pkg1 and chapter02pkg2 in the sample code demonstrate the behavior.


Image A.1The OSGi (Open Services Gateway initiative) infrastructure strengthens the role of packages as components further. A bundle, OSGi’s notion of a module, by default hides all contained classes, and the OSGi class loader actually enforces the restriction. Only the content of packages explicitly mentioned in the bundle’s description, or manifest, will be accessible from the outside. Eclipse exploits this restriction by placing the public class of each component into a public package, and placing the helper classes into separate subpackages named internal by convention.

1.7.2 The Facade Pattern

When designing a component there is always a trade-off between powerful functionality and complexity of use: The more configurations and application scenarios that are supported, the steeper the learning curve before simple uses can be accomplished. For instance, if your Eclipse plugin for enhanced Java editing just wants to prompt the user to select some existing class or interface, you don’t want to be bothered about finding availableImage 7.10 types, extracting their names to a list, filtering the list in a background job, and so on. All you want is a “type selection dialog.” Fortunately, the Eclipse platform offers just the method(s): JavaUI.createTypeDialog(). You specify essentially the context in which the dialog is used, and the method then plugs together and configures the necessary objects from the subsystem.Image 100


Pattern: Facade

You want to facilitate the use of a subsystem by providing a uniform, clean interface that hides the collaboration between objects within the subsystem.

Designate or create a number of public facade classes through which clients can access the subsystem’s services in the most frequent cases.

The facade can also hide the subsystem’s internals, if you make other classes package-visible or, in the context of OSGi, move them to non-public subpackages.


Examples can be found throughout the Eclipse platform. For example, JavaCore provides access to the structured model of the Java source code in the system, and for manipulating aspects of Java projects. The JavaPlugin provides ready-made, reusable elements such as configurations for Java source editors. The ASTParser is really just a facade for Eclipse’s internal Java compiler. The JET2Platform allows you to run Java Emitter Templates,Image 235 which are then used, for instance, by the Eclipse Modeling Framework, without delving into the details of the code generation process.

1.8 Basics of Using Classes and Objects

We have finally reached the core of the matter: How to use objects? Much has already been said about the different elements that objects contain, and about their specific purpose and usage. Having gathered the pieces, we can now turn to the question of arranging them into objects. To avoid redundancy, the presentation here frequently refers to previously introduced material. It can therefore also be read as a restatement of previous principles in a larger context.

As we delve into the question of using objects, you might feel that we are really discussing object-oriented design. Indeed, it won’t do any harm if you take a peek at Section 11.1 if you are getting curious. Design in general is, however, a complex activity that relies on further concepts and technical idioms that will be discussed up to Chapter 11. For now, we will draw the dividing line between analysis and creation: We will look at existing objects to find out how they work and how they achieve their purpose. Strategies for arriving at these objects will have to come later.

In this section and the next two chapters, you will acquire practical blueprints for your work. Together with the design patterns already introduced, they give you a repertoire for being creative with objects. Indeed, an essential prerequisite for becoming a successful designer is being an experienced developer. So don’t hesitate to apply and test your new working knowledge immediately in your current projects.

1.8.1 General Facets of Objects

In the basic mindset of object-oriented programming, we have already seenImage 1.1 essential characteristics of objects: They are small, lightweight, and black-box structures; they focus on specific tasks; and they collaborate to achieve larger goals. Also, they have identity and usually represent entities outside the software world, either from the real world or just concepts we invent while writing the software.

Now it is time to take one step closer and ask: What is it that objects actually do? Speaking very generally, objects will do three kinds of things:Image 263(Ch.4, p.110)

• They maintain information, by storing data in their fields.

• They perform actions, by executing code in their methods and thereby modifying the state of the software (and real) world. They may collaborate with other objects as part of their actions.

• They make decisions relating to other objects, by analyzing available information and then calling back on the other objects to make them perform the corresponding actions.

Usually, an object’s overall behavior contains aspects from all three categories, where decisions about other objects are perhaps less frequent.

However, it is also useful to look out for objects in which one of the aspectsImage 261 predominates. Then, one arrives at role stereotypes:Image 263(p.4, p.159ff)

Image 1.3.3 Image 1.3.3Information holders manage pieces of data important to the application, but do not implement the corresponding logic themselves.Image 7.1 Image 9.3 They may be entirely passive, such as an SWT Rectangle or a JFace Document for storing text. But they may also actively request the information they are supposed to know from others.

Image 1.3.4 Image 1.8.6 Image 7.1Service providers implement special algorithms or application logic, but rely on information obtained elsewhere. For instance, a Layout in SWT computes the position of widgets on the screen, based on constraints attached to these widgets.

Controllers mainly make decisions, but delegate the resulting actions to others rather than carrying them out themselves. In this way, they orchestrate or direct more complex operations. Prototypically, theImage 9.2 controller in the well-known MODEL-VIEW-CONTROLLER pattern receives the user input and decides which application logic must be invoked in response.

Three further roles are specific to the software machinery that is necessary to get the application running:

Image 2.2 Structurers manage groups of objects and their relationships. Think ofImage 7.6 a dialog window, such as Dialog in JFace: It creates a main area and some buttons, and makes sure that the button clicks are received. Similarly, think of a pool of database connections: When a client requests a connection, the pool either creates a new one or reuses an existing one.

Image 1.8.7 Image 2.4Interfacers link together parts of the system that are logically independent, and they connect the system to the outside world, to its users, and to other systems. For instance, they may perform I/O, parse and interpret requests coming in over the network, or transform data passed between different subsystems.

Image 7.7Coordinators mainly pass on requests between other objects. They enable, facilitate, or channel the communication of these objects, thereby centralizing the logic that is behind that communication.

Stereotypes offer a useful characterization of objects from a broad perspective. It is important to keep in mind, though, that they seldom occur in pure form. Instead, service providers typically keep some information themselves, structurers also make decisions on behalf of the managed objects, and interfacers may need to simulate some services, rather then merely delegating requests to a different part of the system.

1.8.2 Service Provider

In a broad sense, all objects provide services to the community: Their services are the justification for their existence. There is no place for “lazyImage 92 classes” or “middle men.” In a more narrow sense, objects collaborate onImage 1.1 specific tasks: One object, the service provider, implements a piece of logic that another object, the client, requires. The client then usually invokes a method on the service provider to avoid reimplementing the logic. One of the core challenges in structuring object-oriented software entails distributing the logic in a way that avoids reimplementation—in other words, inventing suitable service providers.

Service providers can be typical library objects, such as a FileInput Stream. Whenever a client needs to read from a file, it creates such an object and invokes its read() method to access the data. When it is done, it disposes of the service provider. (We leave out precautions for exceptionImage 1.5.6 safety here.)

serviceProvider.ReadingFiles.main


FileInputStream input = new FileInputStream(file);
while ((readCount = input.read(buf)) != -1) {
// do something with data in buf[0..readCount]
}
input.close();


More often, the service provider is part of the application’s object networkImage 1.1 Image 11.1 and clients just contact the desired object whenever they need its services. For instance, a workbench page contains the main content of an Eclipse window (i.e., the window minus menu, toolbar, and status bar). It contains the open editors and their surrounding views, such as the package explorer.Image 12.3.3.4 A workbench page offers, among many other things, the service of showing one more view (or bringing the given view to the front, if it is already shown):

org.eclipse.ui.IWorkbenchPage


public IViewPart showView(String viewId) throws PartInitException;


If you invoke, in the Java editor, Show in view (via Alt-Shift-W), your command is finally dispatched to the following method (greatly abbreviated), which determines the page you are currently working with and asks it to open the target view.

org.eclipse.ui.internal.ShowInHandler


public Object execute(ExecutionEvent event)
throws ExecutionException {
...
IWorkbenchWindow activeWorkbenchWindow = HandlerUtil
.getActiveWorkbenchWindowChecked(event);
...
IWorkbenchPage page = activeWorkbenchWindow.getActivePage();
IViewPart view = page.showView(targetId);
...
}


Image 1.4.3Services are often represented as methods: The client invokes a specific method to get at a specific service, and when the method returns, the service has been performed. One can, however, take a broader view. For instance,Image 2.1 the OBSERVER pattern can be understood as a notification service: The clients register for the notifications with one method call, but these are delivered only later on, when state changes actually occur. This kind ofImage 1.1 service reflects the view on objects as active entities.

Image 1.1When placing services in objects, you should recall two general guidelines.Image 1.4.3 First, think of objects from the outside. Services must be understandableImage 4.2.2 and easily usable for clients who do not know about the object’s internals. In particular, method names, parameters, and return values mustImage 11.2 not give away the internal structure. Second, objects should remain small;Image 205 that is, their services should be logically coherent, and they should not becomeImage 92 bloated with different kinds of services. If you suspect your class isImage 1.4.8.3 getting too heavy, extract parts of it into separate classes.

1.8.3 Information Holder

Objects in general are active: They offer services, they perform tasks, theyImage 1.3.3 react to method calls. A few selected objects nevertheless mainly storeImage 2.1.3 data and have that data manipulated by others. For instance, event objects have the purpose of packaging and transporting information, as can be seen in SWT’s MouseEvent or KeyEvent. Similarly, SWT’s Rectangle and Point capture areas and positions on the screen. In connection withImage 2.4.3 the PROXY pattern, the real subject might be a pure information holder that is managed by the proxy. An example from Eclipse’s Java tooling is SourceMethodElementInfo, which contains data about a method in a Java file. The data is manipulated by a Method object.

A large domain for information holders is the application boundary,Image 201(Ch.37) where data is exchanged with other systems. For instance, the Java Persistence API is used to access relational databases. It deals in entities, whichImage 94(Ch.3) are plain old Java objects that map to database rows. The application logic often resides outside the entities in a separate layer of the application.Image 127 Similarly, the Java Architecture for XML Binding (JAXB) uses objects to represent hierarchically nested XML elements. It is used as the basis for SOAP web-service servers and clients, to shield them from dealing withImage 201(Ch.28) DOM objects directly.

From a system perspective, however, pure information holders are rare.Image 92(“Data Class”) They go against the grain of object-oriented programming, as they throw away the expressiveness of method overriding, and even avoid creatingImage 1.4.1 proper black-box objects. Whenever you design a data object, look out forImage 1.1 functionality that goes well with the data, for active behavior that could—eventually—be sufficiently powerful to hide the data altogether, because no one accesses the data directly anymore.


Place services together with the data that they work on.


For example, the designers of SWT have placed common operations onImage 7 Rectangles as static methods in a separate class Geometry, while theImage 1.4.8.4 designers of the Graphical Editing Framework’s Draw2D layer have chosenImage 214 to enhance their Rectangles with these operations. The neutral guideline can also be phrased somewhat more emphatically:Image 92


When you have a lazy information holder, make it do some work.


1.8.4 Value Object

Information holders may or may not have identity: JPA entities representImage 1.1 specific database rows, while SWT Rectangles are exchangeable as long as their position and extent are the same. Prototypically, Strings are just containers for the characters they manage. Information holders without identity are value objects, meaning that they are nothing but a representation of the stored value. Value objects will usually override equals andImage 1.4.13 hashCode to express the exact conditions under which objects are treated as “the same value” and are therefore interchangeable. For instance, the different Rectangle classes in the Eclipse platform override these methods. Value objects usually support copying or cloning, either through methods or constructors. For this reason:Image 1.6.4


Be sure to compare value objects using equals() instead of ==.



Image The only exceptions are string constants. The method String.intern() yields a unique object with the same character value as the argument, so that interning two equal strings yields the exact same object. Such an object is also called the canonical representation of the value. All string literals, including final static constants, are interned, so that you can compare them by ==. Interning symbols is a common techniqueImage 2 in compiler construction, where declared names are made canonical to speed up the frequent search for names. If you are in a similar situation, you may want to use intern() yourself.


Furthermore, value objects are often immutable: Their fields are final and operations work by creating new instances representing the result values. For instance, BigInteger provides arbitrary precision integers, basedImage 143 on an immutable sign/magnitude representation. The operations then have the usual mathematical meaning, although for syntactic reasons they cannot be written infix:

java.math.BigInteger


public BigInteger add(BigInteger val) {
...
return new BigInteger(resultMag, cmp == signum ? 1 : -1);
}



Image Image 239C++ includes comprehensive support for value objects. Infix operators such as +, -, and so on can be overloaded, so that mathematical operations can be written in familiar notation. Implicit copies, such as in variable initialization and parameter passing, can be coded ascopy constructors, and the syntax a=b is considered as just a special assignment operator, which can likewise be overloaded. Finally, value objects can be stack-allocated, which circumvents, especially for small objects, the possible inefficiency of heap allocation and garbage collection. Beyond that, C++ objects that do not have virtual methods do not have any object header; they contain just the object’s data fields. In consequence, an object wrapping an int stores just that single int.


When using value objects extensively, the programming style and software design change dramatically—when going all the way, one will effectivelyImage 245,42 employ functional programming. The advantages of this paradigm are well known:

• Statements once true for an object remain true, since the object does not change. It will therefore be simpler to argue about correctness.

• Because the correctness arguments are simpler and there are no side effects, debugging becomes simpler as well.

• The computations building on immutable value objects can reflect mathematical rules and algorithms. In many cases, the specification of a method may be expressed directly in its implementation.

Image 196Internal data structures may be shared between objects, which may outweigh the cost of creating new instances and decrease memory consumption.

Image 148(§2.1)Immutable objects are inherently thread-safe, so that no synchronization is required.

Image 44(Item 15)Some argue that these consequences are worth working with immutableImage 194 objects as far as possible. With special language constructs, such as case matching, working with value objects becomes even more attractive.

In the end, the decision of how many immutable objects you use depends on your style of problem solving and thinking. If you feel comfortable with purely functional solutions, try them out.

There is one possible caveat. Many object-oriented frameworks assume mutability, so purely functional solutions may stand alone, and you may end up re-creating, in your own functional framework, much of the logic that is already available in principle.

Conversely, many problem domains lend themselves to a purely functional treatment, and frameworks covering these domains will use immutable objects. For instance, symbolic manipulation of deeply nested trees benefits particularly from structure sharing and therefore from functional programming. For instance, Microsoft’s open-source .NET tooling platform “Roslyn”Image 184 is largely about syntax and code manipulations, in the context of compilation, code transformation, static analysis, and many other operations. Its API therefore uses immutable objects. As a result, syntax rewriting can be done in the style of mathematically precise term rewriting.Image 17

1.8.5 Reusable Functionality

A prime motivation for introducing a class is that it can be instantiated many times, so that its functionality is reused and need not be reimplementedImage 1.4.8.3 in different places. We have already seen such classes in their role as service providers; there, one creates a new instance to access its operations.Image 1.8.2 Of course, reuse also encompasses objects that become part of the application’s network of objects more permanently, such as an Eclipse TextEditorImage 1.1 or an SWT MenuItem. When browsing through the examples given earlier, you will notice that reusable objects are really composed from many parts and helpers. It is, indeed, usually necessary to look beyond single objectsImage 1.7 when trying for reuse:


Components are often a more suitable unit of reuse than individual objects.


Although reuse is highly desirable, there is a fundamental problem: Achieving it is exceedingly difficult.


Plan reusable classes carefully based on extensive experience.


The challenge in creating reusable classes is one of return on investment.Image 12.1.4 When writing “the simplest thing that could possibly work” for your current application, it is clear that you will spend the minimal possible effort on a solution for today’s problems. When writing a reusable class, you must make further investments, since you are creating a solution for tomorrow’s problems as well. Instead of calling back a single object, for example, you may need an OBSERVER pattern; instead of closely collaborating with anImage 2.1.4 object in the neighborhood, you might need to reimplement some functionalityImage 1.7 to make your class a stand-alone entity. The additional effort spent today will be justified only if you actually save effort tomorrow—avoid the smell of “speculative generality.”Image 92

The expected return on investment will occur only if you have predicted the needs of tomorrow’s application correctly. This, in turn, will be more likely if you are experienced in the area you are working in. While you are new to an area, your attempts at reuse will often be futile and frustrating.


Follow the “Rule of Three” when aiming at reuse.


A succinct guideline on the benefits of reusable components is given by the “Rule of Three”: Do not attempt to define an abstraction before you have seen three concrete examples. The rule helps because it covers both the necessity of some previous experience and the expected return on investment in later applications.

After this rather discouraging outlook on reuse, we end on a more positive note:


Look out for potential reuse and discuss it with experienced colleagues.


Reuse will not happen at all unless you actively collect opportunities, and unless you recognize the three examples that make reuse worthwhile. This, in turn, will be possible only if there is a culture of reuse, a culture that values a clear code structure over short-time savings achieved by quick-and-dirty copy-and-paste solutions. You can start such a culture by discussing the opportunities you see, as long as you are prepared to take the advice of experienced colleagues if the opportunity is not so promising after all.

1.8.6 Algorithms and Temporary Data

Image 1.1When looking back at the mindset of object-oriented programming, you will notice that code does not figure very prominently. Objects are entities that often represent things outside the software world; they exhibit identity and behavior, and it is only incidentally that the behavior is captured by code.

In some objects, however, the code is really the main thing, since theyImage 214,269 embody an algorithm. For instance, Zest is a graph drawing library for Draw2D figures in the Graphical Editing Framework. All of its layout algorithms are captured as objects, whose classes derive fromAbstractLayout Algorithm.


Enclose algorithms with their auxiliary data structures as objects.


Bundling an algorithm with its data structures achieves two things. First,Image 1.4.5 the data is readily available throughout subroutines—that is, in private helper methods. Second, and more importantly, the data is encapsulated and hidden from clients. When tomorrow you find a better implementation, you can always replace the old one. Its original presentation also ascribesImage 100 these goals to the STRATEGY pattern.Image 1.3.4


Extract temporary fields with few referencing methods into separate objects.


Very often you recognize such algorithms only after you have coded them as helpers within a larger object. Then, you may sometimes find that their data structures are kept in fields that are valid only while the algorithm is in progress. Rather than forcing the maintenance team to understand at which points which fields are valid, you may extract the algorithm to a separate object, using the tools Extract Class and Move Method to Helper.Image 1.4.8.3 In the best case, you even make your algorithm reusable.Image 1.8.5

Sometimes, the shared temporary fields do not become obvious until you start dissecting a large and complex method into different processing steps.Image 1.4.5 This is the whole point of the METHOD OBJECT pattern:Image 27


Pattern: Method Object

When you find that breaking a complex method into smaller parts requires complex parameter passing, create an object to represent one invocation of the method. The class name reflects the method name, the constructor takes the original method parameters, and the fields hold temporary values.


At a lower level, it is sometimes necessary to wrap code in objects to pass it around, most often as Runnables. If you already have the code, you can ask Eclipse to create the object:


Tool: Surround With

Select the code you wish to pass around in an object and use the menu entry Source/Surround With/Runnable (Alt-S W or Alt-Shift-Z).


1.8.7 Boundary Objects

Objects within a software system work in a protected clean-room environment: They know their collaborators, their respective services, their demandsImage 4.1 and guarantees. As long as there are no bugs, the object machinery runs smoothly and reliably. Interfacing with the outside world is quite a different matter: You cannot rely on files to have the expected structure,Image 1.5.2 Image 4.6 on users to enter only well-formatted data, or on other systems to send only allowed requests over the network. Instead, you must expect the network to break down unexpectedly, the disk to overflow, and the communication partner to be a malicious intruder.

Image 4.6Guarding against these uncertainties requires a lot of effort and fore-thought. The required logic is best kept in specialized boundary objects (Fig. 1.7), so that the core of the system can remain clean and simple.

Image

Figure 1.7 Boundary Objects


Boundary objects implement demarcation lines of trust.


The main goal of boundary objects is to insulate the internal software structure from the vagaries of the outside world. In other words, you trust the objects inside the system, but never the data or requests from outside the system. Boundary objects therefore check incoming data and requests meticulously before passing them on to the core that processes it.


Boundary objects transfer external to internal formats.


Very often, the process of validating incoming data is integrated with the process of translating it to some internal format that matches the processing demands. For instance, Eclipse stores information about a project’s name, builders, and other details in a file .project in each project’s root directory. The ProjectDescriptionReader translates that external XML format into an intern ProjectDescription. In this way, the logic for internalizing the data is localized in one object. Thus, if ever the external format should change, you know which code needs to be adapted.

1.8.8 Nested Classes

Image 1.1The ideal picture of objects is that they are stand-alone entities that collaborate rather loosely, just when others can perform specific tasks more effectively. Unfortunately, this ideal cannot always be reached, simply because a corresponding distribution of tasks is not achieved easily.

Image 2.4.1Suppose one of your classes called A is interested in changes to Eclipse’s Java model, but really only in a special summary, not the details. As depicted in Fig. 1.8(a), it would employ a helper B that analyzes the change reports relative to A’s current state and passes on only a digest. Bthen requires a lot of information from A and even modifies its state directly.Image 11.2 This requires A’s interface to be larger than would be judged from its ownImage 1.7 purpose. Of course, the methods can be package-visible, but still they must be there; or else, uglier still, A’s fields would have to be package-visible. Nested classes allow you to resolve this problem (Fig. 1.8(b)). Specifically, placing B inside A allows it to access A’s fields directly, even if they are private—the outer class’s fields are visible to its nested classes.

Image

Figure 1.8 Motivation for Nested Classes


Nested classes enable proper structuring of closely collaborating objects.


The structure in Fig. 1.8(b) greatly enhances readability and maintainability: A’s fields remain private, yet the nested class B can access them. The scoping rules of Java make it clear that no other class will access A’s internals, so maintainers of A do not need to look further than B to understand the implementation.

Put the other way around, readers will also know that B is not a standalone class at all, but must be understood in connection with and in the context of A.

1.8.8.1 Inner Classes

Inner classes are nested classes that hold an implicit reference to an instance of the outer class, so that they can access that instance’s fields at runtime. In most cases, this happens by referring to a field from the outer class. Explicit references use the syntax OuterClassName.this. Inner classes can occur at class level as (non-static) member classes, inside methods as local classes, and in expressions as anonymous classes. The last case warrants a separate discussion in Section 1.8.8.3.


Inner classes are proper classes. Apply the same care as for top-level ones.


The goal with usual objects is to keep them as independent from one another as possible, so that they can be understood, developed, and maintained independently. You should spend the same effort on your inner classes: Make them as stand-alone as possible, give them their own purpose, chooseImage 1.1 descriptive names. If an inner class is just a lump of code that accessesImage 1.2.3 the outer class’s state all over the place, no further structure is gained and readability is not improved.


Delegate to the outer class to modifiy the data of that class.


Technically, inner classes can both read and write the fields of their surrounding instance. The guideline emphasizes the idea that you should strive to create explicit interfaces between your inner and outer classes wherever possible. If the outer class’s fields are changed only by methods of thatImage 1.1 class, you get a self-contained outer object that can be understood andImage 4.2.3 maintained on its own. In particular, the consistency of the outer object does not depend on the code of the inner class.

Let us study the two guidelines within AbstractTextEditor, which is the base of most editors in Eclipse. Because it has a lot of different aspects, it factors some of them into inner classes. For instance, the user can toggle insert/overwrite mode by some key sequence, which invokes the following action. While the isChecked() method reads the editor’s current state, its run() method does not dare to modify that state, but delegates to the outer class instead.

org.eclipse.ui.texteditor.AbstractTextEditor


class ToggleInsertModeAction extends ResourceAction {
public boolean isChecked() {
return fInsertMode == SMART_INSERT;
}
public void run() {
switchToNextInsertMode();
}
}



Image This class happens to be package-visible in the original sources. It might as well be private, since it is referenced only from within AbstractTextEditor.


The conceptual boundary between inner and outer class becomes especially important in more involved cases that require more logic. You may want to look at the TextListener in Eclipse’s text editor AbstractText Editor for example code; it contains a fair number of fields itself, yet it takes meticulous care to access the outer class only through method calls.


Hide inner classes to express that they are internal.


Inner classes usually depend on the outer class and are not usable outside that context. To emphasize this fact, the classes should not be public in most cases. The only exception would be an aspect or part that obviously contributes to the outer class’s purpose and that you want to share with its clients. It this case, the nested class is often static (see the discussion in the next subsection).


Image The reference to the outer object is usually determined implicitly as the immediately enclosing instanceImage 111(§15.9.2) at the point where the instance of the inner class is created. Briefly speaking, this must take place in some method of the outer object. It is also possible to create instances of inner classes from the outside, at arbitrary points, by making the reference to the outer object explicit in qualified class instance creation expressions:Image 111(§15.9)

Inner innerInstance = outerInstance.new Inner();


Such object creations are usually not desirable. If the inner class is really linked intricately to the outer class, then it should be nobody else’s business to create instances of the inner class. In such cases, it is more readable to make the reference to the outer class explicit and pass it to the inner class’s constructor.


1.8.8.2 Static Member Classes

The goal of nested classes is to express clearly which code can access the outer class’s private aspects. Static member classes—that is, member classes with modifier static—go one step beyond inner classes in that they do not contain an implicit reference to an outer object. The maintenance developer can deduce two things. First, the inner object is stand-alone, so it can be understood without looking at the outer class. Second, the outer object’s state is not accessed implicitly in the inner class, so it can be understood without looking at the inner class.


Static nested classes clarify the insulation between inner and outer objects.


Typical examples are found in helpers that parameterize reusable components.Image 1.3.2 The concrete nested class is specific to its surrounding class, yet conceptually it works inside a different object. For instance, TableViewersImage 9.3.2 use a ViewerComparator to categorize and sort the displayed entries. The clients of table viewers usually have very specific needs, so they subclass ViewerComparator in private nested classes. Conversely, their specific comparators usually do not depend on the clients’ state, so they can be static. For instance, theJARFileSelectionDialog from the Eclipse Java tooling prompts the user for a JAR archive or a folder when specifying build paths for projects. Inside the dialog, a nested class FileViewerComparator keeps folders and files separate:

org.eclipse.jdt.internal.ui.wizards.buildpaths.JARFileSelectionDialog


private static class FileViewerComparator extends ViewerComparator {
public int category(Object element) {
return element instanceof File &&
((File) element).isFile() ? 1 : 0;
}
}


For more examples, open the Type Hierarchy (F4) on ViewerComparator and look out for private and/or static subclasses.

Image 1.3.1A different category of examples occurs in internal, nonreusable data structures. Since these are often passive, the outer object determines what happens and the inner objects do not access the outer one at all. Here is a typical example from the library’s HashTable, whose entries form singly linked lists for collision resolution:

java.util.Hashtable.Entry


private static class Entry<K, V> implements Map.Entry<K, V> {
int hash;
final K key;
V value;
Entry<K, V> next;
}


1.8.8.3 Anonymous Classes

Sometimes objects are required just for technical reasons, but their logicImage 1.4.7 and purpose are negligible. A typical case involves callbacks: You cannot pass a method as a callback, but instead have to construct a whole object. The object is really irrelevant; it is the code that matters. In such cases, Java allows you to define a new class and instantiate it in a single step:

new InterfaceOrClassName() { Field and Method Declarations }


Use anonymous classes as containers for code snippets.


Image 7.1As an example, SelectionListeners are notified, among other things, when a button has been pressed. Here is a typical example from the wizard you get when you invoke Team/Share project on multiple projects. The second page has a list of projects and a button labeled “Share project.” To react to button clicks, the wizard attaches the following listener object:

org.eclipse.team.internal.ui.wizards.ProjectSelectionPage


shareButton.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
shareSelectedProject();
}
public void widgetDefaultSelected(SelectionEvent e) {}
});


Another typical application area of anonymous classes comprises objects that are really functions. For instance, a Comparator is really an order predicate. The following code from a refactoring tool just sorts hits by their positions in the source file:

org.eclipse.jdt.internal.corext.refactoring.code.IntroduceFactoryRefactoring


SearchMatch[] hits = rg.getSearchResults();
Arrays.sort(hits, new Comparator<SearchMatch>() {
public int compare(SearchMatch m1, SearchMatch m2) {
return m2.getOffset() - m1.getOffset();
}
});



Don’t put extensive logic into anonymous classes.


Anonymous classes do not have names that could document or explain their purpose. As a consequence, they are rather hard to understand andImage 1.2.3 maintain if they contain extensive code.


Use variable or method names to specify the anonymous class’s purpose.


In contrast, the context of anonymous classes often implies their purpose. In the SelectionListener example, the button’s variable name, together with the called method’s name, clearly documents the point of the listener. Likewise, the Comparator is used to sort the array hits. There is one more technique along this line, which involves storing the created instance in a field or variable. For instance, the Eclipse platform’s JavaPlugin, among other things, keeps track of Java-related font settings through the following listener:

org.eclipse.jdt.internal.ui.JavaPlugin


fFontPropertyChangeListener = new IPropertyChangeListener() {
public void propertyChange(PropertyChangeEvent event) {
...
}
};



Image Novices are sometimes surprised that they must declare local variables and parameters final in connection with inner classes, and in particular anonymous classes within methods. The reason is this: Conceptually, the inner class should be able to access the surrounding scope, meaning the language’s nesting constructs such as classes, methods, and blocks. Using the compiler-generated reference, they can immediately access the outer object’s fields. Local variables and parameters are a different: Although they are visible from classes inside the method, they reside in the method’s stack frame. Since the compiler cannot store a reference to that stack frame in the inner object, it copies the variables’ values as seen at the point of object creation. To make this very clear, the language prescribes that the variables must, in fact, be final.


1.8.9 The null Object

The object named null is very special. First, it is not a proper object at all, but rather a “non-object.” Since one cannot invoke methods on null, it is actually very hard to work with it. Especially when used as a return value from methods, it burdens the client with a subsequent case distinction. Some authors therefore recommend that you avoid null altogether.Image 172(Ch.7, p.110f)

Many practitioners are not quite so severe. They use null to represent “no object,” “no return value,” “no options given,” “default value,” and other scenarios—just anywhere that the statement “there is no object” has a meaning in itself. As an example of a return value,HashMap.get(key) yields null when no value is associated with the key. The client will be aware that indeed this can happen from a logical perspective, so that theImage 1.3.8 Image 1.6.1 case distinction is useful and sensible anyway. Inside a class, null fields often denote, for instance, “not yet initialized.”

From a pragmatic point of view, one has to consider the alternatives. We focus on return values, but parameters and fields are similar. First, one could throw an exception if “no object” can be returned from a method. This would be justified if it is unexpected or unusual that no object can beImage 1.5 Image 6.3 returned. Since clients must then catch the exception, this solution introduces just a different form of case distinction. In some cases, one can return a “default” or “neutral” object, such as Collections.emptyList(). This has the disadvantage that the client cannot determine whether the result is empty because of a nonexistent object, or because the list is genuinely empty. It works well, however, for objects that merely get called back, asImage 7.10 seen in NullProgressMonitor, which has empty method stubs and does not report progress at all.

Taking the idea of default objects one step further, one can manufacture a proper object that behaves like a nonexistent object. For instance,Image 2.4.3 Eclipse’s resource layer always returns handles to resources: Whether a file on disk exists or not does not matter; getFile() always gives you a proper IFile object that you can work with. When accessing the file’s content, an exception may be thrown, of course, but one expects this anyway with I/O. Also, the client can check exists().

In a variant of this approach, a special Null object may be used toImage 3.1.6 replace explicit case distinctions in the code by method dispatch. The following pattern has been introduced as a refactoring to remove repeatedImage 92 checks for null that clutter the code base:


Pattern: Null Object

If many clients of a class C check for null and then execute some (common) default behavior, introduce a subclass NullC of C and override the methods to exhibit the same default behavior. You can then remove the checks for null in all clients. If checks for null remain necessary because some clients execute a deviating default behavior, introduce a method isNull() in C and have NullC return true, or use instanceof directly.



Image The Objective-C language includes a nil object. It accepts all messages (i.e., provides all methods), but these methods do nothing and return default values.



Image The NULL OBJECT pattern can also be understood as the object-oriented counterpart of introducing artificial sentinel elements into data structures with the aim ofImage 72 reducing case distinction in the operations. For instance, an empty doubly linked list usually contains a single sentinel node, which has its next and previous pointers point to itself. In a non-empty list, the sentinel remains as an extra element. The first node’s previous pointer and the last node’s next pointer both point to the sentinel, and the sentinel points back as expected. The code for insertion and deletion of nodes then does not have to check for these special cases, because the right thing happens automatically. Every node technically has a successor and a predecessor, even if it happens to be the sentinel.


The pragmatic advice therefore is: Don’t use null lightly, since it induces case-distinctions in the code that may blur the code’s meaning and make it more complex than necessary. Go through the alternatives, and decide to use null objects only when you feel that a “no object” can be justified logically.


Image There is one final technical aspect concerning null objects that professional developers must be aware of, because it can lead to memory leaks: Whenever a field or array holds a reference to an object, but that object is no longer logically required, the reference should be set tonull. In this way, the garbage collector can reclaim the object if it is no longer referenced elsewhere. For example, the ArrayList takes care to clean up after removing an element from the sequence, by setting the newly freed slot to null (the comment is from the original sources):


java.util.ArrayList


public E remove(int index) {
...
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}