effective java: classes and interfaces
DESCRIPTION
Effective Java: Classes and Interfaces. Last Updated: Fall 2008. Agenda. Material From Joshua Bloch Effective Java: Programming Language Guide Cover Items 13 through 22 Classes and Interfaces Was Items 12 through 18 in 1 st Edition Moral: Inheritance requires careful programming. - PowerPoint PPT PresentationTRANSCRIPT
Effective Java: Classes and Interfaces
Last Updated: Fall 2008
Classes and Interfaces2
Agenda Material From Joshua Bloch
Effective Java: Programming Language Guide
Cover Items 13 through 22 Classes and Interfaces Was Items 12 through 18 in 1st Edition
Moral: Inheritance requires careful
programming
Classes and Interfaces3
Item 13: Minimize Accessibility of Classes and Members
Standard advice for information hiding/encapsulation Decouples modules Allows isolated development and
maintenance Java has an access control
mechanism to accomplish this goal
Classes and Interfaces4
Make Each Class or Member as Inaccessible as Possible
Standard list of accessibility levels private package-private (aka package friendly) protected public
Huge difference between 2nd and 3rd
package-private: part of implementation protected: part of public API
Classes and Interfaces5
Exception: Exposing Constants
Public constants must contain either primitive values or references to immutable objects
Wrong – Potential Security Hole: public static final Type[] VALUES =
{…}; Problem:
VALUES is final; entries in VALUES are not!
Classes and Interfaces6
Exposing Constants - Solutions
Correct:private static final Type[] PRIVATE_VALUES =
{…};public static final List VALUES = Collections.unmodifiableList(Arrays.asList (PRIVATE_VALUES));
Also Correct:private static final Type[] PRIVATE_VALUES =
{…};public static final Type[] values() { return (Type[]) PRIVATE_VALUES.clone();}
Classes and Interfaces7
Item 14: In Public Classes, Use Accessors, Not Public Fields
Avoid code such as:Class Point { public double x; public double y; }
No possibility of encapsulation Use get/set methods instead:Public double getX() { return x; }Public void setX(double x) { this.x = x}
Advice holds for immutable fields as well Limits possible ways for class to evolve
Classes and Interfaces8
Item 15: Minimize Mutability
Reasons to make a class immutable: Easier to design Easier to implement Easier to use Less prone to error More secure Easier to share Thread safe
Classes and Interfaces9
5 Rules to Make a Class Immutable
Don’t provide any mutators Ensure that no methods can be
overridden (more detail…) Make all fields final (more detail…) Make all fields private Ensure exclusive access to any
mutable components (more detail…)
Classes and Interfaces10
Rule 2: No Overridden Methods
Prevents careless or malicious subclasses from compromising the immutable behavior of the class
How to implement Make class final (easiest) Make methods final (allows subclassing) Make all constructors private or package-
private Requires static factories, which are better
anyway
Classes and Interfaces11
Rule 3: Make All Fields Final
Clearly expresses intent System enforcement of immutability Issues with object instantiation and
thread access Sometimes, making fields final is too
strong Lazy initialization may be preferable
Classes and Interfaces12
Rule 5: Exclusive Access to Mutable Components
Never initialize a mutable field to a client provided reference
Never return a reference to a mutable field
Make defensive copies See Liskov – Exposing the rep
Classes and Interfaces13
Typical Transformation Typical method in mutable class Foo:
public void foo(T1 t1, T2, t2, …) {modify “this”} Immutable version of Foo:
public Foo foo(T1 t1, T2, t2, …) { Foo f = … … return f;}
Functional programming vs. procedural programming.
See Liskov’s Poly for an example
Classes and Interfaces14
Disadvantage: Performance
Typical approach: Provide immutable class Provide mutable companion class for situations
where performance is an issue Clients choose on performance needs Example in Java Library:
String (Immutable) StringBuilder (Companion Mutable Class)
Static factories can cache frequently used items More on this in Liskov 15
Classes and Interfaces15
Item 16: Favor Composition over Inheritance
Issue ONLY for implementation inheritance Interface inheritance does NOT have these
problems Inheritance breaks encapsulation!
Difficult to evolve superclass without breaking subclasses
Difficult for superclass to maintain invariants in face of malicious/careless subclass
Classes and Interfaces16
Example: Broken Subtypepublic class IHashSet extends HashSet private int addCount = 0; // add() calls public IHashSet() {} public IHashSet(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);}}
Classes and Interfaces17
Broken Example, continued
So, what’s the problem? IHashSet s = new IHashSet(); s.addAll(Arrays.asList(new String[] {“Snap”,
“Crackle”, “Pop”})); What does addCount() return?
3? 6?
Internally, HashSet’s addAll() is implemented on top of add(), which is overridden. Note that this is an implementation detail, so we can’t get it right in IHashSet
Classes and Interfaces18
Source of Difficulty: Overridden Methods
Overriding methods can be tricky May break the superclass invariant
Overridden method does not maintain invariant
May break subclass invariant New methods may be added to superclass What if new method matches subclass
method?
Also, recall problems with equals() and hashCode()
Classes and Interfaces19
Composition Fortunately, composition solves all of
these problems – even though it makes the programmer work harder
// Inheritance public Class Foo extends Fie {…} // Composition public class Foo { private Fie f = …; // Note: forwarded methods …}
Classes and Interfaces20
Revisiting the Examplepublic class ISet implements Set { private final Set s; private int addCount = 0; public ISet (Set s) { this.s = s} public boolean add(Object o) { addCount++; return s.add(o); } public boolean addAll (Collection c) { addCount += c.size(); return s.addAll(c); } // forwarded methods from Set interface
Classes and Interfaces21
A more elegant version (without generics – See Bloch for these)
// Reusable Forwarding Classpublic class ForwardingSet implements Set { private final Set s; public ForwardingSet (Set s) { this.s = s;} public void clear() { s.clear();} public boolean contains(Object o) { return s.contains(o);} // plus all the other methods}// Wrapper class – public class ISet extends ForwardingSet { private int addCount = 0; public ISet (Set s) { super(s);} @Override public boolean add(Object o) { addCount++; return super.add(e); } // other instrumented methods}
Classes and Interfaces22
This is Cool! Consider temporarily instrumenting a
Set Note that Set is an interfacestatic void f(Set s) { ISet myS = new ISet (s); // use myS instead of s // all changes are reflected in s!}
Classes and Interfaces23
A Variation That Doesn’t Work
public class ICollection implements Collection { private final Collection c; private int addCount = 0; public ICollection (Collection c) { this.c = c} public boolean add(Object o) {…} public boolean addAll (Collection c) {…} // forwarded methods from Collection interface public boolean equals(Object o) { return c.equals(o); } // Now, we’re dead!
Classes and Interfaces24
This is no longer Cool! Consider temporarily instrumenting a Set
Note: Set is a subtype of CollectionSet s = new HashSet();ICollection t = new ICollection(s);s.equals(t) // c is not a Set, so falset.equals(s) // t.c == s, so true
Issue: Set has a uniform equals contract; Collection does not (and should not…)
Keep this in mind when using this model.
Classes and Interfaces25
Item 17: Design and Document for Inheritance
Or else prohibit it. First, document effects of overriding
any method Document “self use”, as in IHashSet
example This is “implementation detail”, but
unavoidable, since subclass “sees” implementation.
Inheritance violates encapsulation!
Classes and Interfaces26
Efficiency May Require Hooks
protected methods may be required for efficient subclasses.
Example: protected removeRange() method in
AbstractList Not of interest to List clients Only of interest to implementers of
AbstractList – provides fast clear() operation
Alternative – O(n^2) clear operation
Classes and Interfaces27
Inheritance is Forever A commitment to allow inheritance is
part of public API If you provide a poor interface, you
(and all of the subclasses) are stuck with it.
You cannot change the interface in subsequent releases.
Classes and Interfaces28
Constructors Must Not Invoke Overridable Methods
// Problem – constructor invokes overridden m()public class Super { public Super() { m();} public void m() {…};}public class Sub extends Super { private final Date date; public Sub() {date = new Date();} public void m() { // access date variable}}
Classes and Interfaces29
What Is the Problem? Consider the code Sub s = new Sub(); The first thing that happens in Sub()
constructor is a call to constructor in Super()
The call to m() in Super() is overridden But date variable is not yet initialized! Further, initialization in Super m() never
happens! Yuk!
Classes and Interfaces30
Inheritance and Cloneable, Serializable
Since clone() and readObject() behave a lot like constructors, these methods cannot invoke overridable methods either.
Problem – access to uninitialized state For Serializable
readResolve(), writeReplace() must be protected, not private (or they will be ignored in subclass.)
Classes and Interfaces31
Bottom Line Be sure you want inheritance, and
design for it, or Prohibit inheritance
Make class final, or Make constructors private (or package-
private)
Classes and Interfaces32
Item 18: Prefer Interfaces to Abstract Classes
Existing classes can be easily retrofitted to implement a new interface Same is not true for abstract classes
due to single inheritance model in Java Interfaces are ideal for “mixins”
Example: Comparable interface Interfaces aren’t required to form a
hierarchy Some things aren’t hierarchical
Classes and Interfaces33
More Interfaces Wrapper idiom a potent combination
with interfaces See ISet example
Possible to provide skeletal implementations for interfaces java.util does this with
AbstractCollection, AbstractSet, AbstractList, and AbstractMap
Classes and Interfaces34
Example for AbstractListstatic List intArrayAsList(final int[] a) { if (a == null throw new NPE(…); return new AbstractList() { public Object get(int i) { return new Integer(a[i]);} public int size() { return a.length;} public Object set(int i, Object o) { int oldVal = a[i]; a[i] = ((Integer) o).intValue(); return new Integer(oldVal);}};}
Classes and Interfaces35
This Implementation Does a Lot!
The List interface includes many methods.
Only 3 of them are explicitly provided here.
This is an “anonymous class” example Certain methods are overridden
Note that it is possible to add a method to an abstract class in a new release It is not possible to add a method to an
interface
Classes and Interfaces36
Item 19: Use Interfaces Only to Define Types
Example that fails the test: “Constant” interface – avoidpublic interface PhysicalConstants { static final double AVOGADROS = ……}
Why is this bad? Users of a class that implements this interface
don’t care about these constants! Think about the client, not the
implementor
Classes and Interfaces37
Alternative to Constants in Interfaces
Constant utility class public class PhysicalConstants { public static final double
AVOGADROS = …}
Classes and Interfaces38
Item 20: Prefer Class Hierarchies to Tagged Classes
//Tagged Class – vastly inferior to a class hierarchyclass Figure { enum Shape { RECTANGLE, CIRCLE }; final Shape shape; // Tag field double length; double width; // for RECTANGLE double radius; // for CIRCLE Figure (double length, double width) {…} // RECTANGLE Figure (double radius) {…} // CIRCLE double area() { switch (shape) { case RECTANGLE: return length*width; case CIRCLE: return Math.PI*(radius * radius); default: throw new AssertionError(); }}
Classes and Interfaces39
Item 20 Continued: A much better solution
//Class hierarchy replacement for a tagged classabstract class Figure { // Note: NOT instantiable! abstract double area();}class Circle extends Figure { final double radius; Circle(double rad) { radius = rad; } double area() { return Math.PI*(radius*radius); }}class Rectangle extends Figure { final double length; final double width; Rectangle (double len; double wid) { length = len; width = wid; } double area() { return length*width; }}
Classes and Interfaces40
Item 21: Use Function Objects to Represent Strategies
In Java, can’t pass functions directly Only Objects can be arguments
So, pass Objects to pass functions Example: Instances of Comparator
We covered this in Liskov 8
Classes and Interfaces41
Item 22: Favor Static Member Classes Over Nonstatic
Nested classes Defined within another class
Four flavors Static member class Nonstatic member class (inner) Anonymous class (inner) Local class (inner)
Classes and Interfaces42
Static vs NonStatic Static requires no connection to
enclosing instance. Nonstatic always associated with
enclosing instance Possible performance issue Possible desire to create by itself
Hence recommendation to favor static See Iterator implementations
Classes and Interfaces43
Anonymous class – (another) example
// Typical use of an anonymous classArrays.sort (args, new Comparator() { public int compare(Object o1, Object o2)
{ return ((String) o1).length() – (String) o2).length(); }});
Question: Does this satisfy the compare() contract?
Classes and Interfaces44
Local classes Declared anywhere a local variable
may be declared Same scoping rules Have names like member classes May be static or nonstatic
(depending on whether context is static or nonstatic)