DOC PREVIEW
MIT 6 170 - Classes and Interfaces

This preview shows page 1-2 out of 6 pages.

Save
View full document
View full document
Premium Document
Do you want full access? Go Premium and unlock all 6 pages.
Access to all documents
Download any document
Ad free experience
View full document
Premium Document
Do you want full access? Go Premium and unlock all 6 pages.
Access to all documents
Download any document
Ad free experience
Premium Document
Do you want full access? Go Premium and unlock all 6 pages.
Access to all documents
Download any document
Ad free experience

Unformatted text preview:

1 Classes and Interfaces 6.170 Lecture 15 Fall 2005 Classes and interfaces lie at the heart of the Java programming language. In this lecture, we describe guidelines to help you design classes and interfaces that are usable, robust and flexible. This lecture draws from the material in Chapter 4 of Joshua Bloch’s book, Effective Java. Accessibility The single most important factor that distinguishes a well-designed module from a poorly designed one is the degree to which the module hides its internal data and other implementation details from other modules. A well-designed module hides all of its implementation details, cleanly separating its API from its implementation. Modules communicate with each other only through their APIs; further, they don’t know each other’s inner workings. This concept is termed information hiding or encapsulation. The Java programming language has many facilities to aid information hiding. One such facil-ity is the access control mechanism, which determines the accessibility of classes, interfaces, and members. For top-level (non-nested) classes and interfaces, there are only two possible access levels: package-private and public. If a top-level class or interface can be made package-private, it should be. By making it package-private, you make it part of the package’s implementation rather than its exported API, and you can modify it, replace it, or eliminate it in a subsequent release without fear of harming existing clients. If you make it public you are obligated to support it forever to maintain compatibility. For members (fields, methods, nested classes, and nested interfaces) there are four possible access levels, listed here in order of increasing accessibility: private : The member is accessible only inside the top-level class where it is declared. package-private : The member is accessible from any class in the package where it is declared. Technically known as default access, this is the access level you get if no access modifier is specified. protected : The member is accessible from subclasses of the class where it is declared, subject to a few restrictions that we won’t get into, and from any class in the package where it is declared. public : The member is accessible from anywhere. The rule of thumb is that you should make each class or member as inaccessible as possible. After carefully designing your class’ public API, you should make all other members private, if possible. Both private and package-private members are part of the class implementation and do 12 not normally impact its exported API. For members of public classes, a huge increase in accessibility occurs when the access level goes from package-private to protected. A protected member is part of the class’ exported API and must be supported forever. Note that there is one rule that restricts your ability to reduce the accessibility of methods. If a method overrides a superclass method, it is not permitted to have a lower access level in the subclass than it does in the superclass. This is necessary to ensure that an instance of the subclass is usable anywhere that an instance of the superclass is usable. Dangers of Inheritance Inheritance (subclassing) is a powerful way to achieve code reuse, but it is not always the best tool for the job. Used inappropriately, it leads to fragile software. It is safe to use inheritance within a package, where the subclass and superclass implementation are under the control of the same programmer. Unlike method invocation, inheritance breaks encapsulation. A subclass depends on the imple-mentation details of its superclass for its proper function. The superclass’ implementation may change from release to release, and if it does the subclass may break, even though its code has not been touched. The subclass must evolve in tandem with its superclass, unless the superclass’ authors have designed and documented it specifically for the purpose of being extended. Let’s suppose that we have a program that uses a HashSet. To tune the performance of our program, we need to query the HashSet as to how many elements have been added since it was created. This is not to be confused with its current size, which goes down when an element is removed. To provide this functionality, we write a HashSet variant that keeps count of the number of attempted element insertions and exports an accessor for this count. The HashSet class contains two methods capable of adding elements, add and addAll, so we override both of these methods: public class InstrumentedHashSet extends HashSet { // The number of attempted element insertions private int addCount = 0; public InstrumentedHashSet() { } public InstrumentedHashSet(Collection c) { super(c); } public boolean add(Object o) { addCount++; return super.add(o); } public boolean addAll(Collection c) { addCount += c.size(); return super.addAll(c); } public int getAddCount() { return addCount; } } 23 This class looks reasonable, but it doesn’t work. Suppose we create an instance and add three elements using the addAll method: InstrumentedHashSet s = new InstrumentedHashSet(); s.addAll(Arrays.asList(new String[] {"S", "Cr", "P"})); We would expect the getAddCount method to return three at this point, but it returns six. What went wrong? Internally, HashSet’s addAll method is implemented on top of its add method, although HashSet does not document this implementation detail, and it shouldn’t have to. The addAll method in InstrumentedHashSet added three to addCount and then invoked HashSet’s addAll implementation using super.addAll. This in turn invoked the add method, as overriden in InstrumentedHashSet, once for each element. Each of these three invocations added one more to addCount, or a total increase of six: Each element added with the addAll method is double-counted! We could “fix” the subclass by eliminating its override of the addAll method. While the resulting class would work, it would depend for its proper function on the fact that HashSet’s addAll method is implemented on top of its add method. This “self-use” is an implementation detail, not guaranteed to hold in all implementations of HashSet


View Full Document

MIT 6 170 - Classes and Interfaces

Download Classes and Interfaces
Our administrator received your request to download this document. We will send you the file to your email shortly.
Loading Unlocking...
Login

Join to view Classes and Interfaces and access 3M+ class-specific study document.

or
We will never post anything without your permission.
Don't have an account?
Sign Up

Join to view Classes and Interfaces 2 2 and access 3M+ class-specific study document.

or

By creating an account you agree to our Privacy Policy and Terms Of Use

Already a member?