Bloch 4

advertisement
Effective Java, Chapter 4:
Classes and Interfaces
Spring 2013
Agenda

Material From Joshua Bloch


Cover Items 14 through 20



“Classes and Interfaces” Chapter
Was Items 12 through 18 in 1st Edition
Moral:

2
Effective Java: Programming Language Guide
Inheritance requires careful programming
Classes and Interfaces
Item 13: Minimize Accessibility of
Classes and Members

Standard advice for information
hiding/encapsulation



3
Decouples modules
Allows isolated development and maintenance
Java has an access control mechanism to
accomplish this goal
Classes and Interfaces
Make Each Class or Member
as Inaccessible as Possible

Standard list of accessibility levels





Huge difference between 2nd and 3rd


4
private
package-private (aka package friendly)
protected
public
package-private: part of implementation
protected: part of public API
Classes and Interfaces
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:

5
VALUES
is final; entries in
VALUES
are not!
Classes and Interfaces
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();
}
6
Classes and Interfaces
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


Also, public mutable fields are not thread safe
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

7
Limits possible ways for class to evolve
Classes and Interfaces
Example: Questionable Use of
Immutable Public Fields
public final class Time {
private static final int HOURS_PER_DAY = 24;
private static final int MINUTES_PER_HOUR = 60;
public final int hour;
public final int minute;
// Not possible to change rep for class
// But we can check invariants, since fields are final
public Time ( int hour, int minute ) {
if (hour < 0 || hour >= HOURS_PER_DAY)
throw new IllegalArgumentException(“Hour: “ + hour);
if (minute < 0 || minute >= MINUTES_PER_HOUR)
throw new IllegalArgumentException(“Minute: “ + minute);
this.hour = hour;
this.minute = minute;
}
// Remainder omitted
}
8
Classes and Interfaces
Item 15: Minimize Mutability

Reasons to make a class immutable:







9
Easier to design
Easier to implement
Easier to use
Less prone to error
More secure
Easier to share
Thread safe
Classes and Interfaces
Example: Class Complex
public final class Complex {
private final double re;
private final double im;
public Complex (double re, double im) { this.re = re; this.im = im;}
// Accessors with no corresponding mutators
public double realPart()
{ return re; }
public double imaginaryPart() { return im; }
// Note standard “producer” pattern
public Complex add (Complex c) {
return new Complex (re + c.re, im + c.im);
}
// similar producers for subtract, multiply, divide
// implementations of equals() and hashCode() use Double class
}
10
Classes and Interfaces
5 Rules to Make a Class
Immutable





11
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 Interfaces
Rule 2: No Overridden
Methods


Prevents careless or malicious subclasses
from compromising the immutable
behavior of the class
How to implement



12
Make class final (easiest)
Make methods final (allows subclassing)
Make all constructors private or packageprivate

Requires static factories, which are better anyway
Classes and Interfaces
Example: Static Factory for
Complex Class
// Note that this version is no longer a final class
public class Complex {
private final double re;
private final double im;
// private constructor shuts off subclassing
// package friendly constructor would allow local subclassing
private Complex (double re, double im) {
this.re = re; this.im = im;
}
// Static factory – could have lots of possible implementations
public static Complex valueOf (double re, double im) {
return new Complex (re, im);
}
// Remainder as before
}
13
Classes and Interfaces
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

14
Lazy initialization may be preferable
Classes and Interfaces
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
Note what this says about mutability

15
Performance advantage often illusory!
Classes and Interfaces
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;
}


16
Functional programming vs. procedural
programming.
See Poly example
Classes and Interfaces
Disadvantage: Performance

Typical approach:




Clients choose on performance needs
Example in Java Library:



17
Provide immutable class
Provide mutable companion class for situations where
performance is an issue
String (Immutable)
StringBuilder (Companion Mutable Class)
 StringBuffer (Deprecated Companion Mutable Class)
Static factories can cache frequently used items
Classes and Interfaces
Item 16: Favor Composition
over Inheritance

Issue ONLY for implementation inheritance



Inheritance breaks encapsulation!


18
Interface inheritance does NOT have these problems
Interface inheritance is better for lots of reasons…
Difficult to evolve superclass without breaking
subclasses
Difficult for superclass to maintain invariants in face
of malicious/careless subclass
Classes and Interfaces
Example: Broken Subtype
// This is very common, but broken
public class InstrumentedHashSet<E> extends HashSet<E>
private int addCount = 0; // add() calls
public InstrumentedHashSet() {}
public InstrumentedHashSet(Collection<? extends E> c)
{ super(c); }
public boolean add(E o) {
addCount++; return super.add(o);
}
public boolean addAll(Collection<? extends E> c) {
addCount += c.size(); return super.addAll(c);
}
// accessor method to get the count
public int addCount() { return addCount; }
}
19
Classes and Interfaces
Broken Example, continued

So, what’s the problem?
InstrumentedHashSet<String> s =
new InstrumentedHashSet<String>();
s.addAll(Arrays.asList(“Snap”, “Crackle”, “Pop”));

What does



addCount()
return?
3?
6?
Internally, HashSet 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
InstrumentedHashSet
20
Classes and Interfaces
Source of Difficulty:
Overridden Methods

Overriding methods can be tricky

May break the superclass invariant


May break subclass invariant



Overridden method does not maintain invariant
New methods may be added to superclass
What if new method matches subclass method?
Also, recall problems with
equals()
and
hashCode()
21
Classes and Interfaces
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
...
}
22
Classes and Interfaces
Revisiting the Example
// Note that an InstrumentedSet IS-A Set
public class InstrumentedSet<E> implements Set<E> {
private final Set<E> s;
private int addCount = 0;
public InstrumentedSet (Set<E> s) { this.s = s}
public boolean add(E o) {
addCount++; return s.add(o); }
public boolean addAll (Collection<? extends E> c) {
addCount += c.size();
return s.addAll(c);
}
// forwarded methods from Set interface
}
23
Classes and Interfaces
A More Elegant Version
// Wrapper class – uses composition in place of inheritance
public class InstrumentedSet<E> extends ForwardingSet<E> {
private int addCount = 0;
public InstrumentedSet (Set<E> s) { super(s); }
@Override public boolean add(E e)
{ addCount++; return super.add(e); }
@Override public boolean addAll(Collection <? extends E> c)
{ addCount += c.size(); return super.add(c); }
public int getAddCount() { return addCount; }
}
// Reusable Forwarding Class
public class ForwardingSet<E> implements Set<E> {
private final Set<E> s;
public ForwardingSet (Set<E> s) { this.s = s;}
public void clear()
{ s.clear();}
public boolean contains(E o)
{ return s.contains(o);}
// plus forwards of all the other Set methods
24 }
Classes and Interfaces
This is Cool!


Consider temporarily instrumenting a Set
Note that Set is an interface
static void f(Set<Dog> s) {
InstrumentedSet<Dog> myS = new InstrumentedSet<Dog> (s);
// use myS instead of s
// all changes are reflected in s!
}
25
Classes and Interfaces
A Variation That Doesn’t Work
// Note that this uses the “basic” version,
// but extending a “ForwardingCollection” doesn’t
// work either, for the same reason
public class InstrumentedCollection<E> implements Collection<E> {
private final Collection<E> c;
private int addCount = 0;
public InstrumentedCollection (Collection<E> c) { this.c = c}
public boolean add(E o) {…}
public boolean addAll (Collection<? extends E> c) {…}
// forwarded methods from Collection interface
public boolean equals(Object o)
{ return c.equals(o); }
// Now, we’re dead!
}
26
Classes and Interfaces
This is no longer Cool!

Consider temporarily instrumenting a
Set
Set is a subtype of Collection
Set<Dog> s = new HashSet<Dog>();
InstrumentedCollection<Dog> t =
new InstrumentedCollection<Dog>(s);
s.equals(t)
// t is not a Set, so false
t.equals(s)
// t.c == s, so true



27
Note:
Issue:

Set has a uniform equals() contract

Collection does not (and should not…)
Keep this in mind when using this model.
Classes and Interfaces
Item 17: Design and
Document for Inheritance


Or else prohibit it.
First, document effects of overriding any
method


28

Document “self use”, as in InstrumentedHashSet
example
This is “implementation detail”, but
unavoidable, since subclass “sees”
implementation.
Inheritance violates encapsulation!
Classes and Interfaces
Efficiency May Require Hooks


protected methods may be required for
efficient subclasses.
Example:




29
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 Interfaces
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.
TEST for inheritance

30
How? By writing subclasses
Classes and Interfaces
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}
}
31
Classes and Interfaces
What Is the Problem?

Consider the code
Sub s = new Sub();





32
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 Interfaces
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

33
Or they will be ignored in subclass.
Classes and Interfaces
Bottom Line


Be sure you want inheritance, and design
for it, or
Prohibit inheritance


34
Make class final, or
Make constructors private (or packageprivate)
Classes and Interfaces
Item 18: Prefer Interfaces to
Abstract Classes

Existing classes can be easily retrofitted to
implement a new interface


Interfaces are ideal for “mixins”


Example: Comparable interface
Interfaces aren’t required to form a
hierarchy

35
Same is not true for abstract classes due to
single inheritance model in Java
Some things aren’t hierarchical
Classes and Interfaces
More Interfaces

Wrapper idiom a potent combination with
interfaces


Possible to provide skeletal implementations
for interfaces

36
See ISet example
java.util does this with AbstractCollection,
AbstractSet, AbstractList, and AbstractMap
Classes and Interfaces
Example for AbstractList
// Concrete implementation built on abstract skeleton
static List<Integer> intArrayAsList(final int[] a) {
if (a == null) throw new NPE(…);
// Note anonymous class
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);
}
};
}
37
Classes and Interfaces
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


Note that it is possible to add a method to
an abstract class in a new release

38
Certain methods are overridden
It is not possible to add a method to an
Classes and Interfaces
interface
Item 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?


39
Users of a class that implements this interface don’t
care about these constants!
Think about the client, not the implementor
Classes and Interfaces
Alternative to Constants in
Interfaces

Constant utility class
public class PhysicalConstants {
public static final double AVOGADROS = ...
}
40
Classes and Interfaces
Item 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) {
// Gag! Roll-your-own dispatching!
case RECTANGLE: return length*width;
case CIRCLE: return Math.PI*(radius * radius);
default: throw new AssertionError();
}
}
41
Classes and Interfaces
Item 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 * (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 Interfaces
42 }
Item 21: Use Function Objects
to Represent Strategies

In Java, you can’t pass functions directly


Only Objects can be arguments
So, pass Objects to pass functions
// Strategy interface
public interface Comparator<T> { public int compare ( T t1, T t2); }
// Question: Is compare() consistent with equals()
Class StringLengthComparator implements Comparator <String> {
private StringLengthComparator() {}
public static final StringLengthComparator
INSTANCE = new StringLengthComparator();
public int compare (String s1, String s2) {
return s1.length – s2.length;
}
}
43
Classes and Interfaces
Item 22: Favor Static Member
Classes Over Nonstatic

Nested classes


Four flavors




44
Defined within another class
Static member class
Nonstatic member class (inner)
Anonymous class (inner)
Local class (inner)
Classes and Interfaces
Static vs NonStatic


Static requires no connection to enclosing
instance.
Nonstatic always associated with
enclosing instance




45
Possible performance issue
Possible desire to create by itself
Hence recommendation to favor static
See Iterator implementations
Classes and Interfaces
Local classes




46
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)
Classes and Interfaces
Download