2. Item 15: Minimize the accessibility of
classes and members
Information hiding or encapsulation is important – decouples the components
Make each class or member as inaccessible as
possible.
3. Item 15: Minimize the accessibility of
classes and members
If a top-level class or interface can be made package-private, it should be.
public class A {...}
top-level class
class A {...}
package-private classs
4. Item 15: Minimize the accessibility of
classes and members
If class or interface is used by only one class, consider making it a private static
nested class of the sole class that uses it.
public class A {
....
private static class B {
...
}
}
B is ONLY used by A
5. Item 15: Minimize the accessibility of
classes and members
It is acceptable to make a private member of a public class package-private in
order to test it
public class A {
private void method() {...}
}
public class A {
void method() {...}
}
Cannot be tested Can be tested by Test Class
under the same package
6. Item 15: Minimize the accessibility of
classes and members
Instance fields of public classes should rarely be public.
public class A {
public String value1;
protected String value2;
}
public class A {
private String value1;
private String value2;
public String getValue1() {
return this.value1;
}
public String getValue() {
return this.value2;
}
}
Not invariants, can be
modified by outside or
subclass.
7. Item 15: Minimize the accessibility of
classes and members
It is wrong for a class to have a public static final array field, or an accessor that
returns such afield.
public class A {
public final static Thing[] VALUES = {...};
}
public class A {
private final static Thing[] VALUES = {...};
public static final Thing[] values() {
return VALUES;
}
}
WRONG! Security hole!,
public class A {
private final static Thing[] PRIVATE_VALUES = {...};
public static final List<Thing> values() {
return Collections
.unmodifiableList(
Arrays.asList(PRIVATE_VALUES));
}
public static final Thing[] values() {
return PRIVATE_VALUES.clone();
}
}
8. Item 15: Minimize the accessibility of
classes and members
Use Java 9 module system to share classes among packages within a module
without making them visible to the entire world.
10. Item 16: In public classes, use accessor
methods, not public fields
class Point {
public double x;
public double y;
}
class Point {
private double x;
private double y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
public double getX() { return x; }
public double getY() { return y; }
public void setX(double x) { this.x = x; }
public void setY(double y) { this.y = y; }
}
NO encapsulation!!
• if a class is accessible outside its package (public class), provide accessor methods.
• public classes should never expose mutable fields.
11. Item 16: In public classes, use accessor
methods, not public fields
class A {
...
private class B {
public int value;
}
}
class A {
int value;
}
if a class is package-private or is a private nested class, there is
nothing inherently wrong with exposing its data fields
private nested class
package-private class
12. Item 16: In public classes, use accessor
methods, not public fields
END
13. Item 17: Minimize mutability
How to make a immutable class:
1. Don’t provide methods that modify the object’s state (No mutators/setters)
2. Ensure that the class can’t be extended.
3. Make all fields final.
4. Make all fields private
5. Ensure exclusive access to any mutable components
14. Item 17: Minimize mutability
public final class Invoice {
private final String invoiceNo;
private final Integer total;
private final List<Item> itemList;
public Invoice(String invoiceNo, Integer total, List itemList) {
this.invoiceNo = invoiceNo;
this.total = total;
this.itemList = itemList;
}
public String getInvoiceNo() {
return this.invoiceNo;
}
public Integer getTotal() {
return this.total;
}
public List<Item> getItemList() {
return this.itemList.stream()
.map(Item::clone) // deep copy list elements
.collect(Collectors.toList());
}
}
Make all fields final
Make all fields private
No mutators/setters
Class can't be extended
Ensure exclusive access to any mutable components
15. Item 17: Minimize mutability
Immutable objects are simple.
Immutable objects are thread-safe
immutable objects can be shared freely.
Exactly one state.
Guarantee invariants.
Require no synchronization
Encourage clients to reuse existing instances wherever possible
16. Item 17: Minimize mutability
An immutable class can provide static factories (Item 1) that cache frequently
requested instances to avoid creating new instances when existing ones would do.
(Singleton)
public final class A {
private static final A instance1 = new A(...);
private A(...) {
...
}
public static A getInstance1(...) {
return instance1;
}
}
static factory method
cache
17. Item 17: Minimize mutability
// Immutable class with static factories instead of constructors
public class Complex {
private final double re;
private final double im;
private Complex(double re, double im) {
this.re = re;
this.im = im;
}
public static Complex valueOf(double re, double im) {
return new Complex(re, im);
}
... // Remainder unchanged
}
Flexible immutable class alternative design.
18. Item 17: Minimize mutability
• You never have to make defensive copies of immutable objects.
• You need not and should not provide a clone method or copy constructor
(Item 13) on an immutable class
• Classes should be immutable unless there’s a very good reason to make
them mutable.
• If a class cannot be made immutable, limit its mutability as much as
possible
• Declare every field private final unless there’s a good reason to do
otherwise.
• The major disadvantage of immutable classes is that they require a separate
object for each distinct value. it is costly. -> Use mutuable companion class
(e.g. String and StringBuilder)
20. Item 18: Favor composition over
inheritance
Inheriting from ordinary concrete classes across package boundaries is dangerous. Unlike
method invocation, inheritance violates encapsulation.
The superclass’s implementation may change from release
to release, and if it does, the subclass may break, even though its code has not
been touched.
21. Item 18: Favor composition over
inheritance
// Broken - Inappropriate use of inheritance!
public class InstrumentedHashSet<E> extends HashSet<E> {
// The number of attempted element insertions
private int addCount = 0;
public InstrumentedHashSet() {
}
public InstrumentedHashSet(int initCap, float loadFactor) {
super(initCap, loadFactor);
}
@Override
public boolean add(E e) {
addCount++;
return super.add(e);
}
@Override
public boolean addAll(Collection<? extends E> c) {
addCount += c.size();
return super.addAll(c);
}
public int getAddCount() {
return addCount;
}
}
InstrumentedHashSet<String> s = new InstrumentedHashSet<>();
s.addAll(List.of("Snap", "Crackle", "Pop")); // 3 elements
System.out.println(s.getAddCount()); // but print 6
public boolean addAll(Collection<? extends E> c) {
boolean modified = false;
for (E e : c)
if (add(e))
modified = true;
return modified;
}
The addAll() method in InstrumentedHashSet added
three to addCount and then invoked HashSet’s
addAll() implementation using super.addAll(). This in
turn invoked the add() method, as overridden in
InstrumentedHashSet
22. Item 18: Favor composition over
inheritance
Instead of extending an existing class, give your new class a private
field that references an instance of the existing class. This design is
called composition because the existing class becomes a component
of the new one.
23. Item 18: Favor composition over
inheritance
// 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.addAll(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(Object o) { return s.contains(o); }
public boolean isEmpty() { return s.isEmpty(); }
public int size() { return s.size(); }
public Iterator<E> iterator() { return s.iterator(); }
public boolean add(E e) { return s.add(e); }
public boolean remove(Object o) { return s.remove(o); }
public boolean containsAll(Collection<?> c)
{ return s.containsAll(c); }
public boolean addAll(Collection<? extends E> c)
{ return s.addAll(c); }
public boolean removeAll(Collection<?> c)
{ return s.removeAll(c); }
public boolean retainAll(Collection<?> c)
{ return s.retainAll(c); }
public Object[] toArray() { return s.toArray(); }
public <T> T[] toArray(T[] a) { return s.toArray(a); }
@Override public boolean equals(Object o)
{ return s.equals(o); }
@Override public int hashCode() { return s.hashCode(); }
@Override public String toString() { return s.toString(); }
}
composition-and-forwarding approach
24. Item 18: Favor composition over
inheritance
Decorator pattern
InstrumentedSet
(concrete decorator)
Set
(component)
ForwardingSet
(decorator)
HashSet
(concrete component)
extends
implments
and wrap
implments
25. Item 18: Favor composition over
inheritance
• Is every B really an A? If you cannot truthfully answer yes to this question, B should
not extend A.
• Does the class that you contemplate extending have any flaws in its API? If so, are
you comfortable propagating those flaws into your class’s API?
Before using inheritance, ask yourself:
27. Item 19: Design and document for
inheritance or else prohibit it
The class must document its self-use of overridable methods.
Use Javadoc tag @implSpec to generate description for a method that invokes
overridable methods.
Test your designed for inheritance class by writing subclasses before you release it.
28. Item 19: Design and document for
inheritance or else prohibit it
Constructors must not invoke overridable methods, directly or indirectly.
public class Super {
// Broken - constructor invokes
an overridable method
public Super() {
overrideMe();
}
public void overrideMe() {
}
}
public final class Sub extends Super {
// Blank final, set by constructor
private final Instant instant;
Sub() {
instant = Instant.now();
}
// Overriding method invoked by
superclass constructor
@Override public void overrideMe() {
System.out.println(instant);
}
}
public static void main(String[] args) {
Sub sub = new Sub(); // null
sub.overrideMe(); // 2020-05-03T08:31:15.449012300Z
}
29. Item 19: Design and document for
inheritance or else prohibit it
It is generally not a good idea for a class designed for inheritance to implement
Cloneable and Serializable because they place a sub-stantial burden on programmers
who extend the class.
The clone() and readObject() methods behave a lot like constructors, neither clone() nor
readObject() may invoke an overridable method, directly or
indirectly.
In the case of readObject(), the overriding method will run before the subclass’s state
has been deserialized.
In the case of clone(), the overriding method will run before the subclass’s clone()
method has a chance to fix the clone’s state.
30. Item 19: Design and document for
inheritance or else prohibit it
If you decide to implement Serializable in a class designed for inheritance and the class has
a readResolve() or writeReplace() method, you must
make the readResolve() or writeReplace() method protected rather than private, or they
will be silently ignored by subclasses.
public class InheritableClass implemetns Serializable {
...
protected Object readResolve() throws ObjectStreamException {
....
}
protected Object writeReplace() throws ObjectStreamException {
....
}
}
31. Item 19: Design and document for
inheritance or else prohibit it
Prohibit subclassing in classes that are not designed and documented to be safely
subclassed.
• Declare the class final.
• Make all constructors private, use factory method instead.
public final class ProhibitExtensionClass {
public ProhibitExtensionClass() {...}
}
public class ProhibitExtensionClass {
private ProhibitExtensionClass() {...}
public static ProhibitExtensionClass createInstance() {
return new ProhibitExtensionClass();
}
}
32. Item 19: Design and document for
inheritance or else prohibit it
END
33. Item 20: Prefer interfaces to abstract
classes
Existing classes cannot, in general, be retrofitted to extend a new abstract class.
public class ExsistingClassA extends ExsistingAbstractClass {
@Override
void existingMethod() {...}
@Override
void newMethod() {...}
}
public class ExsistingClassB extends NewAbstractClass {
@Override
void newMethod() {...}
}
public abstract class ExsistingAbstractClass extends NewAbstractClass {
abstract void existingMethod();
}
public abstract class NewAbstractClass {
abstract void newMethod();
}
New abstract class have
to place it high up in the
type hierarchy
34. Item 20: Prefer interfaces to abstract
classes
Existing classes can easily be retrofitted to implement a new interface
public class ExsistingClassA
extends ExsistingAbstractClass implements NewInterface {
@Override
void existingMethod() {...}
@Override
public void newMethod() {...}
}
public class ExsistingClassB implements NewInterface {
@Override
public void newMethod() {...}
}
public abstract class ExsistingAbstractClass {
abstract void existingMethod();
}
public interface NewInterface {
void newMethod();
}
35. Item 20: Prefer interfaces to abstract
classes
Interfaces allow for the construction of nonhierarchical type frameworks.
public interface SingerSongwriter extends Singer,
Songwriter {
AudioClip strum();
void actSensitive();
}
public interface Songwriter {
Song compose(int chartPosition);
}
public interface Singer {
AudioClip sing(Song s);
}
36. Item 20: Prefer interfaces to abstract
classes
• Skeletal implementation class (AbstractInterface)
• e.g. Collections Framework: AbstractCollection, AbstractSet, AbstractList, AbstractMap
• Skeletal implementations are designed for inheritance, good documentation is absolutely essential in a
skeletal implementation (item 19)
• If you export a nontrivial interface, you should strongly consider providing a skeletal implementation to
go with it
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
}
public abstract class AbstractList<E>
extends AbstractCollection<E> implements List<E> {
)
public interface List<E> {
}
Skeletal implementation class
(AbstractInterface)
38. Item 21: Design interfaces for posterity
interface ExistingInterface {
void existingMethod();
default void additionMethod() { // Java 8 default method
// default implementation...
}
}
Java 8 default method allows the addition of methods to exsiting interfaces.
Many new default methods were added to the core collection interfaces in Java 8,
primarily to facilitate the use of lambdas
39. Item 21: Design interfaces for posterity
Implementation Requirements:
The default method implementations (inherited or otherwise) do not apply any
synchronization protocol. If a Collection implementation has a specific synchronization
protocol, then it must override default implementations to apply that protocol.
org.apache.commons.collections4.collection.SynchronizedCollection implements
Collections
but Java 8 Collections default method removeIf() do not apply any synchronization
protocol
If client call removeIf() method on a SynchronizedCollection instance,
ConcurrentModificationException may result.
40. Item 21: Design interfaces for posterity
In the presence of default methods, existing implementations of an inter-
face may compile without error or warning but fail at runtime.
Using default methods to add new methods to existing interfaces should be
avoided unless the need is critical
It is still of the utmost importance to design interfaces with great
care.
42. Item 22: Use interfaces only to define
types
// Constant interface antipattern - do not use!
public interface PhysicalConstants {
// Avogadro's number (1/mol)
static final double AVOGADROS_NUMBER = 6.022_140_857e23;
// Boltzmann constant (J/K)
static final double BOLTZMANN_CONSTANT = 1.380_648_52e-23;
// Mass of the electron (kg)
static final double ELECTRON_MASS = 9.109_383_56e-31;
}
The constant interface pattern is a poor use of interfaces.
• Leak implementation.
• Confuse users.
• Unnecessary commitments
Will these constants be used by all subclasses in the future?
43. Item 22: Use interfaces only to define
types
END
44. Item 23: Prefer class hierarchies to
tagged classes
// Class hierarchy replacement for a tagged class
abstract class Figure {
abstract double area();
}
class Circle extends Figure {
final double radius;
Circle(double radius) { this.radius = radius; }
@Override
double area() {
return Math.PI * (radius * radius);
}
}
class Rectangle extends Figure {
final double length;
final double width;
Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
@Override double area() { return length * width; }
}
// Tagged class - vastly inferior to a class hierarchy!
class Figure {
enum Shape { RECTANGLE, CIRCLE };
// Tag field - the shape of this figure
final Shape shape;
// These fields are used only if shape is RECTANGLE
double length;
double width;
// This field is used only if shape is CIRCLE
double radius;
// Constructor for circle
Figure(double radius) {
shape = Shape.CIRCLE;
this.radius = radius;
}
// Constructor for rectangle
Figure(double length, double width) {
shape = Shape.RECTANGLE;
this.length = length;
this.width = width;
}
double area() {
switch(shape) {
case RECTANGLE: return length * width;
case CIRCLE: return Math.PI * (radius * radius);
default: throw new AssertionError(shape);
}
}
}
46. Item 24: Favor static member classes
over nonstatic
If a nested class would be useful in some other context, then it should be a top-level
class.
There are four kinds of nested classes:
1. static member classes,
2. nonstatic member classes,
3. anonymous classes,
4. local classes.
If you declare a member class that does not require access to an enclosing
instance, always put the static modifier in its declaration
48. Item 25: Limit source files to a single
top-level class
Never put multiple top-level classes or
interfaces in a single source file.
// Two classes defined in one file (Utensil.java). Don't ever do this!
class Utensil {
static final String NAME = "pan";
}
class Dessert {
static final String NAME = "cake";
}
49. Item 25: Limit source files to a single
top-level class
END