22 Feb 2021
You can find the shorter version of this summary here.
Item 36: Use EnumSet instead of bit fields
- Bit fields are inefficient and using it decreases readability. The EnumSet class combines the conciseness and performance of bit fields with all the many advantages of enum types described in Item 34. Use enum set instead.
Item 37: Use EnumMap instead of ordinal indexing
- Ordinals are subject to change and compilers cannot check its usage. It is rarely appropriate to use ordinals to index into arrays: use EnumMap instead.
Item 38: Emulate extensible enums with interfaces
- Enums are not extensible so create an interface and make subtype enums implement that interface to mimic extensible enum.
Item 39: Prefer annotations to naming patterns
- Annotation processor and compiler knows about annotation but not naming patterns.
- Most programmers will have no need to define annotation types. But all programmers should use the predefined annotation types that Java provides.
Item 40: Consistently use the Override annotation
- The compiler can protect you from a great many errors if you use the Override annotation on every method declaration that you believe to override a supertype declaration,
Item 41: Use marker interfaces to define types
- A marker interface is an interface that contains no method declarations but merely designates (or “marks”) a class that implements the interface as having some property. (e.g.
Serializable)
Item 42: Prefer lambdas to anonymous classes
- lambdas are clearer and more concise.
- Omit the types of all lambda parameters unless their presence makes your program clearer.
Item 43: Prefer method references to lambdas
- When you use method reference, you can eliminate boilerplate codes. The more parameters a method has, the more boilerplate you can eliminate with a method reference.
- The more parameters a method has, the more boilerplate you can eliminate with a method reference.
Item 44: Favor the use of standard functional interfaces
- This will make your API easier to learn, by reducing its conceptual surface area, and will provide significant interoperability benefits, as many of the standard functional interfaces provide useful default methods.
Item 45: Use streams judiciously
- Stream pipelines are evaluated lazily: evaluation doesn’t start until the terminal operation is invoked, and data elements that aren’t required in order to complete the terminal operation are never computed.
- Overusing streams makes programs hard to read and maintain.
09 Feb 2021
You can find the shorter version of this summary here.
Item 26: Don’t use raw types
- it is legal to use raw types (generic types without their type parameters), but you should never do it. If you use raw types, you lose all the safety and expressiveness benefits of generics.
- use
<?> instead.
Item 27: Eliminate unchecked warnings
- When you get warnings that require some thought, persevere! Eliminate every unchecked warning that you can. If you eliminate all warnings, you are assured that your code is typesafe, which is a very good thing.
- If you can’t eliminate a warning, but you can prove that the code that provoked the warning is typesafe, then (and only then) suppress the warning with an @SuppressWarnings(“unchecked”) annotation.
Item 28: Prefer lists to arrays
- Arrays and generics have very different type rules. Arrays are covariant and reified; generics are invariant and erased. As a consequence, arrays provide runtime type safety but not compile-time type safety, and vice versa for generics. As a rule, arrays and generics don’t mix well. If you find yourself mixing them and getting compile-time errors or warnings, your first impulse should be to replace the arrays with lists.
Item 29: Favor generic types
- Generic types are safer and easier to use than types that require casts in client code. When you design new types, make sure that they can be used without such casts.
Item 30: Favor generic methods
- Generic methods, like generic types, are safer and easier to use than methods requiring their clients to put explicit casts on input parameters and return values. Like types, you should make sure that your methods can be used without casts, which often means making them generic.
Item 31: Use bounded wildcards to increase API flexibility
- For maximum flexibility, use wildcard types on input parameters that represent producers or consumers.
- PECS stands for producer-extends, consumer-super.
Item 32: Combine generics and varargs judiciously
- Varargs and generics do not interact well because the varargs facility is a leaky abstraction built atop arrays, and arrays have different type rules from generics.
- If you choose to write a method with a generic (or parameterized) varargs parameter, first ensure that the method is typesafe, and then annotate it with @SafeVarargs so it is not unpleasant to use.
Item 33: Consider typesafe heterogeneous containers
- When you need more flexibility, for example, a database row can have arbitrarily many columns, and it would be nice to be able to access all of them in a typesafe manner. Luckily, there is an easy way to achieve this effect. The idea is to parameterize the key instead of the container. Then present the parameterized key to the container to insert or retrieve a value. The generic type system is used to guarantee that the type of the value agrees with its key.
Item 34: Use enums instead of int constants
- Enums are more readable, safer, and more powerful. Many enums require no explicit constructors or members, but others benefit from associating data with each constant and providing methods whose behavior is affected by this data.
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.
17 Jan 2021
You can find the shorter version of this summary here.
Item 21: Design interfaces for posterity
-
Using default methods to add new methods to existing interfaces should be avoided unless the need is critical, in which case you should think long and hard about whether an existing interface implementation might be broken by your default method implementation.
-
Default methods are, however, extremely useful for providing standard method implementations when an interface is created, to ease the task of implementing the interface.
it is critically important to test each new interface before you release it. Multiple programmers should implement each interface in different ways. At a minimum, you should aim for three diverse implementations.
Item 22: Use interfaces only to define types
- constant interface - an interface contains no methods and consists solely of static final fields (constants) should be avoided.
Item 23: Prefer class hierarchies to tagged classes
In short, tagged classes are verbose, error-prone, and inefficient. (less readable, more boilerplate, more memory footprint, non-final states…)
Item 24: Favor static member classes over nonstatic
- If you declare a member class that does not require access to an enclosing instance, always put the static modifier in its declaration, making it a static rather than a nonstatic member class. If you omit this modifier, each instance will have a hidden extraneous reference to its enclosing instance.
Item 25: Limit source files to a single top-level class
- While the Java compiler lets you define multiple top-level classes in a single source file, there are no benefits associated with doing so, and there are significant risks.
10 Jan 2021
You can find the shorter version of this summary here.
Item 16: In public classes, use accessor methods, not public fields
-
The classes whose data fields are accessed directly do not offer the benefits of encapsulation. You can’t change the representation without changing the API, you can’t enforce invariants, and you can’t take auxiliary action when a field is accessed.
-
Never expose mutable field in public classes - the class loses control over the field.
Item 17: Minimize mutability
To make a class immutable, follow these five rules:
-
Don’t provide methods that modify the object’s state (known as mutators).
-
Ensure that the class can’t be extended.
-
Make all fields final.
-
Make all fields private.
-
Ensure exclusive access to any mutable components.
Why:
-
Immutable objects are simple. An immutable object can be in exactly one state, the state in which it was created.
-
Immutable objects are inherently thread-safe; they require no synchronization.
-
Not only can you share immutable objects, but they can share their internals.
-
Immutable objects make great building blocks for other objects,
-
Immutable objects provide failure atomicity for free
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.
Item 18: Favor composition over inheritance
-
Inheritance violates encapsulation.
-
It is hard to enforce correct implementation rules to inheriting classes - method overriding can lead to unexpected result.
-
Parent class may add additional methods - child class that never changed can cause unexpected results.
- Instead of extending an existing class, give your new class a private field that references an instance of the existing class.
inheritance is powerful, but it is problematic because it violates encapsulation. It is appropriate only when a genuine subtype relationship exists between the subclass and the superclass. Even then, inheritance may lead to fragility if the subclass is in a different package from the superclass and the superclass is not designed for inheritance. To avoid this fragility, use composition and forwarding instead of inheritance, especially if an appropriate interface to implement a wrapper class exists. Not only are wrapper classes more robust than subclasses, they are also more powerful.
Item 19: Design and document for inheritance or else prohibit it
It is so easy for other engineers to be “wrong” when inheriting your class so design and document for inheritance, or else prohibit it.
-
The only way to test a class designed for inheritance is to write subclasses. Test your class by writing subclasses before you release it.
-
Constructors must not invoke overridable methods, directly or indirectly.
-
Designing a class for inheritance requires great effort and places substantial limitations on the class.
Item 20: Prefer interfaces to abstract classes
-
A major difference is that to implement the type defined by an abstract class, a class must be a subclass of the abstract class.
-
Existing classes can easily be retrofitted to implement a new interface. All you have to do is to add the required methods, if they don’t yet exist, and to add an implements clause to the class declaration. Existing classes cannot, in general, be retrofitted to extend a new abstract class. If you want to have two classes extend the same abstract class, you have to place it high up in the type hierarchy where it is an ancestor of both classes. Unfortunately, this can cause great collateral damage to the type hierarchy, forcing all descendants of the new abstract class to subclass it, whether or not it is appropriate.
-
You can, however, combine the advantages of interfaces and abstract classes by providing an abstract skeletal implementation class to go with an interface. The interface defines the type, perhaps providing some default methods, while the skeletal implementation class implements the remaining non-primitive interface methods atop the primitive interface methods. Extending a skeletal implementation takes most of the work out of implementing an interface. This is the Template Method pattern.
04 Jan 2021
You can find the shorter version of this summary here.
Item 11: Always override hashCode when you override equals
You must override hashCode in every class that overrides equals.
- If two objects are equal according to the equals(Object) method, then calling hashCode on the two objects must produce the same integer result.
equal objects must have equal hash codes.
-
A good hash function tends to produce unequal hash codes for unequal instances. Ideally, a hash function should distribute any reasonable collection of unequal instances uniformly across all int values.
-
Don’t provide a detailed specification for the value returned by hashCode, so clients can’t reasonably depend on it; this gives you the flexibility to change it.
Item 12: Always override toString
-
The general contract for toString says that the returned string should be “a concise but informative representation that is easy for a person to read.”
-
Providing a good toString implementation makes your class much more pleasant to use and makes systems using the class easier to debug.
-
Override Object’s toString implementation in every instantiable class you write, unless a superclass has already done so.
Item 13: Override clone judiciously
-
A class implementing Cloneable is expected to provide a properly functioning public clone method. In order to achieve this, the class and all of its superclasses must obey a complex, unenforceable, thinly documented protocol. The resulting mechanism is fragile, dangerous, and extralinguistic: it creates objects without calling a constructor.
-
You are usually better off providing an alternative means of object copying. A better approach to object copying is to provide a copy constructor or copy factory.
Item 14: Consider implementing Comparable
-
Virtually all of the value classes in the Java platform libraries, as well as all enum types (Item 34), implement Comparable.
-
whenever you implement a value class that has a sensible ordering, you should have the class implement the Comparable interface so that its instances can be easily sorted, searched, and used in comparison-based collections.
-
When comparing field values in the implementations of the compareTo methods, avoid the use of the < and > operators. Instead, use the static compare methods in the boxed primitive classes or the comparator construction methods in the Comparator interface.
Item 15: Minimize the accessibility of classes and members
-
This concept, known as information hiding or encapsulation, is a fundamental tenet of software design.
-
Information hiding is important for many reasons, most of which stem from the fact that it decouples the components that comprise a system, allowing them to be developed, tested, optimized, used, understood, and modified in isolation.
The rule of thumb is simple: make each class or member as inaccessible as possible.
If you make it public, you are obligated to support it forever to maintain compatibility.
- It is acceptable to make a private member of a public class package-private in order to test it, but it is not acceptable to raise the accessibility any higher.