Table of contents
- Item 34: Use enums instead of int constants
- Item 35: Use instance fields instead of ordinals
- Item 36: Use EnumSet instead of bit fields
- Item 37: Use EnumMap instead of ordinal indexing
- Item 38: Emulate extensible enums with interfaces
- Item 39: Prefer annotations to naming patterns
- Item 40: Consistently use the @Override annotation
- Item 41: Use marker interfaces to define types
Two special-purpose families of reference types: Enums and Annotations.
Item 34: Use enums instead of int constants
int constants
public static final int APPLE_FUJI = 0;
Disadvantages:
- No type safety (other values can be used, even if not present in enum)
- Expressiveness (nothing descriptive printed in logs, only values)
- No namespace (prefix required)
- Constant variables -> compiled (high coupling) to client code -> brittle programs
- Performance problems (
String
constants uses string comparison)
Java enum
They are full-fledged classes and much more powerful than the ones provided by C, C++ or C#.
Basic Idea:
- export each instance via
public static final
field - no accessible constructor => effectively final => no instantiation or extension (they implicitly extend
java.lang.Enum
thus no more extensions possible) - instance controlled
- generalization of Singletons
Advantages:
- compile-time type safety
- namespace, same names can co-exist with different namespace
- add or reorder constants, without recompiling clients
- descriptive printable strings
- add associated, arbitrary methods, fields, implement interface
- high quality implementation of
Object
methods,implements Comparable, Serializable
- useful
static
methods likevalues()
,valueOf()
,name()
etc.
constant-specific method implementations:
- declare an
abstract
method inenum
and override it in the constant with constant-specific implementation. - to allow reuse we can provide a default implementation instead and override only when needed.
- prefer this instead of switch statements, as they have to changed every time, someone may just forget
- consider the strategy enum pattern if some, but not all, enum constants share common behaviours (can use
private
nested enum for this).
switch in enum
- augmenting enum types with constant-specific behavior, useful if enum not in our control
- if enum is in control, but method doesn't belong in enum type
enum constructors can't access static
fields except constant variables because when constructor is being executed the static
fields wouldn't have initialised. Similarly, enum constants can't access other enum constants from their constructor.
Item 35: Use instance fields instead of ordinals
Never derive a value associated with an enum
from its ordinal; store it in an instance field instead. The Enum specification has this to say about ordinal: “Most programmers will have no use for this method. It is designed for use by general-purpose enum based data structures such as EnumSet
and EnumMap
.”
Item 36: Use EnumSet
instead of bit fields
The EnumSet
class combines the conciseness and performance of bit fields with all the many advantages of enum types. It extends the Set
interface.
Item 37: Use EnumMap
instead of ordinal indexing
It is rarely appropriate to use ordinals to index into arrays: use EnumMap
instead. If the relationship you are representing is multidimensional, use EnumMap<..., EnumMap<...>>
. This is a special case of the general principle that application programmers should rarely, if ever, use Enum.ordinal. EnumMap
use arrays internally, so we pay little in space or time cost for the added clarity, safety, and ease of maintenance.
Item 38: Emulate extensible enums with interfaces
While we cannot write an extensible enum type, we can emulate it by writing an interface
to accompany a basic enum
type that implements the interface
. This allows clients to write their own enums (or other types) that implement the interface. Instances of these types can then be used wherever instances of the basic enum
type can be used, assuming APIs are written in terms of the interface
.
<T extends Enum<T> & Operation>
here extends means that T
is a subtype of Enum
and Operation
as its not possible to extend more than one class.
Item 39: Prefer annotations to naming patterns
There is simply no reason to use naming patterns when you can use annotations instead. Naming patterns were used earlier like in testing the method should start with Test which makes the code brittle as someone may misspell and the test would be silently ignored. Annotations provide a much robust method, having features of repeatable inside containers etc.
Item 40: Consistently use the @Override
annotation
We should use the @Override
annotation on every method declaration that we believe to override a superclass declaration to avoid overloading or overriding a non-existent method (may be in future). It gives helpful compile time error.
Item 41: Use marker interfaces to define types
A marker interface is an interface
that contains no method declarations like Serializable
.
Marker interfaces are not obsolete after marker annotations because:
- they define a type
- they can be targeted more precisely
Make a choice between annotation and interface when using Element.TYPE
in annotation. If you want marking other than class
/interface
then use annotation. If you do want to define a type, do use an interface.
This is it!