Vous êtes sur la page 1sur 46

Effective Java, Chapter 4:

Classes and Interfaces


Last Updated: Fall 2011

Agenda

Material From Joshua Bloch

Cover Items 14 through 20

Classes and Interfaces Chapter


Was Items 12 through 18 in 1st Edition

Moral:

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

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
Standard
list of accessibility levels
Possible
private

Huge difference between 2nd and 3rd

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:

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();
}

Classes and Interfaces

Item 14: In Public Classes,


Use Accessors, Not Public
Avoid
Fields
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

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
}

Classes and Interfaces

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 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

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)
Classes and Interfaces

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 packageprivate

12

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:

Provide immutable class


Provide mutable companion class for situations
where performance is an issue

Clients choose on performance needs


Example in Java Library:

(Immutable)
StringBuilder (Companion Mutable Class)
String

17

StringBuffer (Deprecated Companion Mutable Class)

Static factories can cache frequently used


items
Classes and Interfaces

Item 16: Favor


Composition over
Issue
ONLY for implementation inheritance
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, whats 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 cant 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 Doesnt


Work
// Note that this uses the basic version,
// but extending a ForwardingCollection doesnt
// 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, were dead!
}

26

Classes and Interfaces

This is no longer Cool!

Consider temporarily instrumenting a

Note:

Set

is a subtype of

Set

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

Issue:

27

has a uniform equals() contract


Collection does not (and should not)
Set

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:

protected

Not of interest to List clients


Only of interest to implementers of
AbstractList provides fast clear() operation
Alternative O(n^2) clear() operation

29

removeRange()
AbstractList

method in

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
// Problem constructor invokes overridden m()
Methods
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 arent required to form a


hierarchy

35

Same is not true for abstract classes due


to single inheritance model in Java

Some things arent hierarchical


Classes and Interfaces

More Interfaces

Wrapper idiom a potent combination


with interfaces

See ISet example

Possible to provide skeletal


implementations for interfaces

36

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


interface
Classes and Interfaces

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


dont 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

42

//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

Item 21: Use Function


Objects to Represent
In Strategies
Java, you cant 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
Nested classes
Nonstatic

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

Vous aimerez peut-être aussi