Title: Effective Java: Classes and Interfaces
1Effective Java Classes and Interfaces
- Last Updated Fall 2008
- SWE 332
2Agenda
- Material From Joshua Bloch
- Effective Java Programming Language Guide
- Cover Items 16 through 20
- Part of Classes and Interfaces Chapter
- Was Items 14 through 18 in 1st Edition
- Moral
- Inheritance requires careful programming
3Item 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
4Example Broken Subtype
- public class IHashSetltEgt extends HashSetltEgt
- private int addCount 0 // add() calls
- public IHashSet()
- public IHashSet(Collectionlt? extends Egt c)
- super(c)
- public boolean add(E o)
- addCount return super.add(o)
-
- public boolean addAll(Collectionlt? extends Egt
c) - addCount c.size() return
super.addAll(c) -
5Broken Example, continued
- So, whats the problem?
- IHashSetltStringgt s new IHashSetltStringgt()
- s.addAll(Arrays.asList(Snap, Crackle,
Pop)) - What does addCount() return?
- 3?
- 6?
- Internally, HashSets addAll() is implemented on
top of add(), which is overridden. Note that
this is an implementation detail, so we cant get
it right in IHashSet -
6Source 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()
7Composition
- 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
-
8Revisiting the Example
- public class ISetltEgt implements SetltEgt
- private final SetltEgt s
- private int addCount 0
- public ISet (SetltEgt s) this.s s
- public boolean add(E o)
- addCount return s.add(o)
- public boolean addAll (Collectionlt? extends Egt
c) - addCount c.size()
- return s.addAll(c)
- // forwarded methods from Set interface
9This is Cool!
- Consider temporarily instrumenting a Set
- Note that Set is an interface
- static void f(SetltDoggt s)
- ISet myS new ISetltDoggt (s)
- // use myS instead of s
- // all changes are reflected in s!
10A Variation That Doesnt Work
- public class ICollectionltEgt implements
CollectionltEgt - private final CollectionltEgt c
- private int addCount 0
- public ICollection (CollectionltEgt c) this.c
c - public boolean add(E o)
- public boolean addAll (Collectionlt? extends Egt
c) - // forwarded methods from Collection interface
- public boolean equals(Object o)
- return c.equals(o) // Now, were
dead!
11This is no longer Cool!
- Consider temporarily instrumenting a Set
- Note Set is a subtype of Collection
- SetltDoggt s new HashSetltDoggt()
- ICollectionltDoggt t new ICollectionltDoggt(s)
- s.equals(t) // c is not a Set, so false
- t.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.
12Item 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!
13Efficiency 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(n2) clear operation
14Inheritance 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. - TEST for inheritance by writing subclasses
15Constructors 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
16What 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!
17Inheritance 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.)
18Bottom Line
- Be sure you want inheritance, and design for it,
or - Prohibit inheritance
- Make class final, or
- Make constructors private (or package-private)
19Item 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 arent required to form a hierarchy
- Some things arent hierarchical
20More 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
21Example for AbstractList
- static List intArrayAsList(final int a)
- if (a null throw new NPE()
- return new AbstractList()
- public Object get(int i)
- return new Integer(ai)
- public int size() return a.length
- public Object set(int i, Object o)
- int oldVal ai
- ai ((Integer) o).intValue()
- return new Integer(oldVal)
22This 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
23Item 19 Use Interfaces Only to Define Types
- Example that fails the test
- Constant interface avoid
- public interface PhysicalConstants
- static final double AVOGADROS
-
- Why is this bad?
- Users of a class that implements this interface
dont care about these constants! - Think about the client, not the implementor
24Alternative to Constants in Interfaces
- Constant utility class
- public class PhysicalConstants
- public static final double AVOGADROS
-
25Item 20 Prefer Class Hierarchies to Tagged
Classes
- //Tagged Class vastly inferior to a class
hierarchy - class 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 lengthwidth
- case CIRCLE return Math.PI(radius
radius) - default throw new AssertionError()
-
26Item 20 Continued A much better solution
- //Class hierarchy replacement for a tagged class
- abstract 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(radiusradius)
-
-
- class Rectangle extends Figure
- final double length final double width
- Rectangle (double len double wid) length
len width wid - double area()
- return lengthwidth
-