Enums and Annotations
Two special purpose families of reference types: Enums and Annotations.
- 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
Item 34: Use enums instead of int constants
public static final int APPLE_FUJI = 0;
- 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 (
Stringconstants uses string comparison)
They are full-fledged classes and much more powerful than the ones provided by C, C++ or C#.
- export each instance via
public static finalfield
- no accessible constructor => effectively final => no instantiation or extension (they implicitly extend
java.lang.Enumthus no more extensions possible)
- instance controlled
- generalization of Singletons
- 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
implements Comparable, Serializable
constant-specific method implementations:
- declare an
enumand 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
privatenested 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
Item 36: Use
EnumSet instead of bit fields
EnumSet class combines the conciseness and performance of bit fields with all the many advantages of enum types. It extends the
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
<T extends Enum<T> & Operation> here extends means that
T is a subtype of
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
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
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
interface then use annotation. If you do want to define a type, do use an interface.
This is it!