Title: Effective Java: Classes and Interfaces
1Effective Java Classes and Interfaces
2Agenda
- 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
3Item 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
4Make 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
5Exception 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!
6Exposing 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()
-
7Item 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
8Item 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
95 Rules to Make a Class Immutable
- Dont 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)
10Rule 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
11Rule 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
12Rule 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
13Typical 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 Liskovs Poly for an example
14Disadvantage 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
15Item 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
16Example Broken Subtype
- public 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)
17Broken Example, continued
- So, whats the problem?
- IHashSet s new IHashSet()
- s.addAll(Arrays.asList(new String 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 -
18Source 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()
19Composition
- 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
-
20Revisiting the Example
- public 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
21A more elegant version (without generics See
Bloch for these)
// Reusable Forwarding Class public 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) _at_Override public boolean
add(Object o) addCount return
super.add(e) // other instrumented
methods
22This is Cool!
- Consider temporarily instrumenting a Set
- Note that Set is an interface
- static void f(Set s)
- ISet myS new ISet (s)
- // use myS instead of s
- // all changes are reflected in s!
23A Variation That Doesnt 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, were
dead!
24This is no longer Cool!
- Consider temporarily instrumenting a Set
- Note Set is a subtype of Collection
- Set s new HashSet()
- ICollection t new ICollection(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.
25Item 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!
26Efficiency 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
27Inheritance 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.
28Constructors 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
29What 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!
30Inheritance 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.)
31Bottom Line
- Be sure you want inheritance, and design for it,
or - Prohibit inheritance
- Make class final, or
- Make constructors private (or package-private)
32Item 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
33More 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
34Example 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)
35This 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
36Item 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
37Alternative to Constants in Interfaces
- Constant utility class
- public class PhysicalConstants
- public static final double AVOGADROS
-
38Item 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()
-
39Item 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
-
40Item 21 Use Function Objects to Represent
Strategies
- In Java, cant pass functions directly
- Only Objects can be arguments
- So, pass Objects to pass functions
- Example Instances of Comparator
- We covered this in Liskov 8
41Item 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)
42Static 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
43Anonymous class (another) example
- // Typical use of an anonymous class
- Arrays.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?
44Local 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)