1. Effective Java (Second Edition)
CHAPTER 4: Classes and Interfaces
presented by
Chandan Benjaram
2. Agenda
Item 13: Minimize the accessibility of classes and members
Item 14: In public classes, use accessor methods, not public fields
Item 15: Minimize mutability
Item 16: Favor composition over inheritance
Item 17: Design and document for inheritance or else prohibit it
Item 18: Prefer interfaces to abstract classes
Item 19: Use interfaces only to define types
Item 20: Prefer class hierarchies to tagged classes
Item 21: Use function objects to represent strategies
Item 22: Favor static member classes over non-static
3. Item-13: Minimize the accessibility of classes and
members
A well designed API should hide all of its implementation details(a.k.a,
internals) and thus component inter-communication should happen
through a well declared API standards (protocols). Thus in general, it
should encourage highest possible cohesion and loose coupling, if any!
Point-I: Make each class member as inaccessible as possible
How you do it?
➡use package private/public on top-outer classes
➡use access modifiers for members (private, package private,
protected, public)
Leaks?
➡security can be leaked if class implements ‘Serializable’
Fix:
➡use ‘transient’ keyword
4. Point-II: Instance fields should never be public
Problem:
//Potential security hole!
public static final Type[] VALUES = { ... };
Fix:
private static final Type[] PRIVATE_VALUES = { ... };
public static final List VALUES = Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));
Point-III: Public classes should not contain any public fields with the exception of
immutable public static final fields
5. Item-14: Public classes should encourage use of
accessors/mutators instead of public fields
➡if a class is accessible outside its package, provide accessors
➡incase of package private/private nested classes, it is OK to expose
fields through ‘public’ access modifiers
6. Item-15: Minimize mutability
What is immutable class?
➡its a, very simple to write, class whose instance can not be
modified once initialized
➡maintains a single state across all calls, thus thread safe by
default
How to make a class immutable?
➡avoid mutators (use functional approach instead!)
➡make it non-extendable
➡make all fields final
➡make all fields private
➡ensure exclusive access to any mutators, if any
Ex: Use defensive copying techniques, readObject, etc.
Note: beware of nested classes! (possible security hole)
7. Advantages of immutable classes:
➡each object belongs to a single state avoiding any complex state
transformations
➡by default, they are thread-safe. thus no synchronization needed
➡effective pooling, caching can be performed with the aid of
different techniques and patterns
Ex: Factory pattern.
Suggestion:
➡never provide a copy constructor/clone methods (String.copy()
violates)
Disadvantages:
➡each distinct value object requires its own object increasing
memory footprint and GC demand
➡problem becomes more worse when working with complicated
multistage operations on large objects
8. Solution:
➡memory footprint and GC demand problem can be limited by
sharing class self internals.
Ex: BigInteger (signum-magnitude)
public BigInteger negate()
Returns a BigInteger whose value is (-this).
➡nicely designed public companion classes can handle multistage
operations problem very smoothly
Ex: StringBuilder for immutable class, String
finally:
➡classes should be immutable unless there is a precise reason to
make mutable
➡make every field as final unless there is a precise reason to avoid
➡fully initialize object with all its required invariants either using
constructor/static factories but not with any public helpers
Hint: Builder pattern can save your life!
9. Item-16: Favor Composition over Inheritance
But, why?
➡violates encapsulation
➡parent private fields can not be accessible if needed!
Solution:
➡Use Composition pattern + Forwarding Methods, which may be seen
as Decorator pattern (loosely speaking!)
10. but wait, there is a problem:
➡it may cause wrapped object blind in viewing wrapper object. thus
Callback frameworks fails to function as expected. A ‘SELF problem’.
11. When to prefer inheritance then?
➡if you can precisely establish a ‘is-a’ relationship between sub-class
and super-class then go for it!
JDK violators- Properties (is-not-a HashTable), Stack (is-not-a
Vector), etc.
Finally:
➡read API docs thoroughly for any flaws, violations, limitations, etc.,
before implementing an interface
12. Item-17: Design and document for Inheritance or
else prohibit it
➡class must document its self-use of override-able methods
➡parent private fields can not be accessible if needed!
➡a class designed for inheritance must be reviewed thoroughly. once
shipped, it would be impossible to make changes without breaking
own clients
➡constructors must not invoke override-able methods
Example:
a super class
13. a subclass
Finally:
➡incase of no options other than self-use of override-able methods,
use self specific code to private helpers
14. Item-18: Prefer interfaces to abstract classes
➡as java permits only single inheritance, a abstract class type
definition is more constrained vs an interface definition
➡abstract class hierarchies may cause ‘combinatorial explosion’
➡on the other hand, interfaces are ideal for mixins. thus adds more
type definitions to primary type
➡existing classes can be easily retrofitted to implement new
interface
➡design your interface with outmost case. once it is shipped & used,
it can not be possible to change method signature or add more
without breaking its clients
➡preferably, provide a ‘skeletal implementation’(abstract interface) of
your interface to go along with it
Ex: AbstractMap<K,V>, AbstractList<E>, etc. (many)
15. Item 19: Use interfaces only to define types
➡interfaces should be only used to define mixin type
➡avoid designing constant interfaces
Why?
-> you are entering into a client commitment
-> pollutes inheritance hierarchy for namespaces
// DONT DO THIS
// CONSTANT INTERFACE
package edu.ej.ch14;
public interface Interface1{
static String NAME_PREFIX = "_";
}
//CLASS IMPLEMENTING INTERFACE
public class Class1 implements Interface1 {
@Override
public String toString() {
return NAME_PREFIX + Class1.class.getName();
}
}
// A TEST HELPER
class Tester1 {
public static void main(String[] args) {
Class1 clazz1 = new Class1();
System.out.printf("Class1#toString: %1$s", clazz1.toString());
}
}
// OUT PUT
Class1#toString: _edu.ej.ch14.Class1
16. Contd...
JDK violators?
java.io.ObjectStreamConstants
What to do with constants?
-> use enum type constants
-> use helper constant classes
-> you can use ‘static import’ to avoid name qualifications
JDK followers?
java.lang.Integer, java.lang.Double, etc.
17. Item 20: Prefer class hierarchies to tagged
classes
➡they provide a multi datatype benefit
18. Contd...
➡kinda verbose, error-prone, inefficient, and may violate inheritance
➡limits the use of type detectors (‘instanceof’/‘isassignablefrom’)
➡a simple fix can be replacing tagged class with class hierarchy as:
// 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;
}
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;
}
double area() {
return length * width;
}
}
19. Item 21: Use function objects to represent
strategies
➡function object: an object which performs actions on other
objects using self methods
➡primarily used to implement Strategy Pattern
Strategy Pattern (GOF def):
Define a family of algorithms, encapsulate each one, and make them interchangeable.
[The] Strategy [pattern] lets the algorithm vary independently from clients that use
it.
➡help to interchangeably operate generically
20. Contd...
➡how we do it?
package edu.ej.ch14.item20;
import java.io.Serializable;
// DEFINE AN INTERFACE
public interface CustomComparator<T> {
int compare(T arg0, T arg1);
}
class Helper{
// STRING TYPE IMPLEMENTATION
private static class StringComparator implements CustomComparator<String> {
@Override
public int compare(String arg0, String arg1) {
// comparing lexicographically
return arg0.compareTo(arg1);
}
}
// BYTE TYPE IMPLEMENTATION
private static class ByteComparator implements CustomComparator<Byte>, Serializable {
@Override
public int compare(Byte arg0, Byte arg1) {
return arg0.compareTo(arg1);
}
}
// CACHED IMPLEMENTATIONS WRAPPED AS INTERFACE TYPE
public static final CustomComparator<String> STRING_COMP= new StringComparator();
public static final CustomComparator<Byte> BYTE_COMP= new ByteComparator();
}
21. Contd...
➡tester
class Tester {
public static void main(String[] args) {
System.out.printf("STRING result: %1$d %nBYTE result: %2$d", Helper.STRING_COMP.compare("x", "x"),
Helper.BYTE_COMP.compare(Byte.valueOf("1"), Byte.valueOf("3")));
}
}
// OUTPUT
STRING result: 0
BYTE result: -2 (OBSERVER THIS)
➡how I did?
➡defined an interface for strategy (comparing)
➡created 2 separate concrete strategy implementations
(StringComparator, ByteComparator)
➡I could have done anonymous implementation, but it limits my
mixin types
➡encapsulated strategy implementations and exported as helper
constants to strategy type.
22. Item 22: Favor static member classes over non-
static
➡There are 4 types of nested classes:
1) static member classes
2) non-static member classes
3) anonymous classes
4) local classes
What are they for?
1) static member classes:
➡a static class that’s declared inside other class(enclosing class)
➡has access to enclosing class’s members
➡obeys general contract as static members
➡generally used as public helpers (recall POJO Builder pattern!)
2) non-static member classes:
-> each instance is associated with enclosing instance
-> a non-modifiable association to enclosing class is established
when member classes is created
-> you can create instance of member class in 2 ways, a) calling
constructor of member class from enclosing class instance
member b)outerInstance.new InnerClass(...)
23. Contd...
2) non-static member classes (contd...):
-> commonly used as Adaptors for enclosing class
Ex: java.util.Map.values()
-> member class instance always requires an enclosing class
instance
-> it forces you to allocate enclosing class instance even if you
do not need it!
-> always prefer static member classes incase you do not have
to access instance members!
3) anonymous classes:
-> has no name
-> declared and instantiated at point of use
-> limits your type checking capabilities
-> clients can not invoke any functions
-> can not support multiple types
-> generally used to create function objects, process objects,
within static factories
Ex: new Comparator(compare()...omitted)
24. Contd...
4) local classes:
-> same as local variables in terms of place and rules
-> ideally, should be kept under fewer lines
-> creates instances that are tied to enclosing class