Skip to content

Instantly share code, notes, and snippets.

@gupta-himanshu
Last active August 6, 2018 09:25
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gupta-himanshu/a1d8cbb07e6444d29aef49419fac592b to your computer and use it in GitHub Desktop.
Save gupta-himanshu/a1d8cbb07e6444d29aef49419fac592b to your computer and use it in GitHub Desktop.
  1. Prefer static factory methods over constructors

    • Advantage - They are not required to create a new object each time they’re invoked. This allows immutable classes to use preconstructed instances, or to cache instances as they’re constructed, and dispense them repeatedly to avoid creating unnecessary duplicate objects.

    • Disadvantage - The classes without public or protected constructors cannot be subclassed. For example, it is impossible to subclass any of the convenience implementation classes in the Collections Framework. Arguably this can be a blessing in disguise because it encourages programmers to use composition instead of inheritance, and is required for immutable types.

  2. Prefer a builder when there are too many Constructor parameters

    • Advantage - It combines the safety of the telescoping constructor pattern with the readability of the JavaBeans pattern. It is a form of the Builder pattern. Instead of making the desired object directly, the client calls a constructor (or static factory) with all of the required parameters and gets a builder object. Then the client calls setter-like methods on the builder object to set each optional parameter of interest. Finally, the client calls a parameterless build method to generate the object, which is typically immutable. The builder is typically a static member class of the class it builds.

    • Disadvantage - In order to create an object, we must first create its builder. While the cost of creating this builder is unlikely to be noticeable in practice, it could be a problem in performance-critical situations.

  3. Always enforce the singleton property with a private constructor or an enum type

    • Summary -

      • Private Constructor - It clear that the class is a singleton: the public static field is final, so it will always contain the same object reference.

      • Enum type - It is more concise, provides the serialization machinery for free, and provides an ironclad guarantee against multiple instantiation, even in the face of sophisticated serialization or reflection attacks.

  4. Always enforce noninstantiability with a private constructor

    • Summary - Attempting to enforce noninstantiability by making a class abstract does not work. The class can be subclassed and the subclass instantiated. Furthermore, it misleads the user into thinking the class was designed for inheritance. There is, however, a simple idiom to ensure noninstantiability. A default constructor is generated only if a class contains no explicit constructors, so a class can be made noninstantiable by including a private constructor.
  5. Prefer dependency injection over hardwiring resources

    • Summary - Static utility classes and singletons are inappropriate for classes whose behavior is parameterized by an underlying resource. What is required is the ability to support multiple instances of the class, each of which uses the resource desired by the client. A simple pattern that satisfies this requirement is to pass the resource into the constructor when creating a new instance. This is one form of dependency injection.
  6. Avoid creating unnecessary objects

    • Summary - You can often avoid creating unnecessary objects by using static factory methods in preference to constructors on immutable classes that provide both. For example, the factory method Boolean.valueOf(String) is preferable to the constructor Boolean(String), which was deprecated in Java 9. The constructor must create a new object each time it’s called, while the factory method is never required to do so and won’t in practice. In addition to reusing immutable objects, you can also reuse mutable objects if you know they won’t be modified. Some object creations are much more expensive than others. If you’re going to need such an “expensive object” repeatedly, it may be advisable to cache it for reuse. Unfortunately, it’s not always obvious when you’re creating such an object.

      • While String.matches is the easiest way to check if a string matches a regular expression, it’s not suitable for repeated use in performance-critical situations.

      • Autoboxing blurs but does not erase the distinction between primitive and boxed primitive types.

      • Prefer primitives to boxed primitives, and watch out for unintentional autoboxing

  7. Eliminate obsolete object references

    • Summary - Memory leaks in garbage-collected languages (more properly known as unintentional object retentions) are insidious. If an object reference is unintentionally retained, not only is that object excluded from garbage collection, but so too are any objects referenced by that object, and so on. Even if only a few object references are unintentionally retained, many, many objects may be prevented from being garbage collected, with potentially large effects on performance. The fix for this sort of problem is simple: null out references once they become obsolete.

      • Nulling out object references should be the exception rather than the norm.

      • Whenever a class manages its own memory, the programmer should be alert for memory leaks

      • Another common source of memory leaks is caches.

      • A third common source of memory leaks is listeners and other callbacks.

  8. Avoid finalizers and cleaners

    • Summary - Finalizers are unpredictable, often dangerous, and generally unnecessary. Their use can cause erratic behavior, poor performance, and portability problems. Finalizers have a few valid uses, which we’ll cover later in this item, but as a rule, you should avoid them. As of Java 9, finalizers have been deprecated, but they are still being used by the Java libraries. The Java 9 replacement for finalizers is cleaners. Cleaners are less dangerous than finalizers, but still unpredictable, slow, and generally unnecessary.

      • Never do anything time-critical in a finalizer or cleaner

      • Never depend on a finalizer or cleaner to update persistent state.

      • There is a severe performance penalty for using finalizers and cleaners.

      • Finalizers have a serious security problem: they open your class up to finalizer attacks.

      • Throwing an exception from a constructor should be sufficient to prevent an object from coming into existence; in the presence of finalizers, it is not.

      • To protect nonfinal classes from finalizer attacks, write a final finalize method that does nothing.

      • Just have your class implement AutoCloseable

  9. Prefer try-with-resources over try-finally

    • Summary - Always use try -with-resources in preference to try - when working with resources that must be closed. The resulting code is shorter and clearer, and the exceptions that it generates are more useful. The try - with-resources statement makes it easy to write correct code using resources that must be closed, which was practically impossible using try - finally.
  10. Obey the general contract when overriding equals

    • Each instance of the class is inherently unique

    • There is no need for the class to provide a “logical equality” test.

    • A superclass has already overridden equals , and the superclass behavior is appropriate for this class.

    • The class is private or package-private, and you are certain that its equals method will never be invoked.

    • The equals method implements an equivalence relation. It has these properties:

      • Reflexive: For any non-null reference value x , x.equals(x) must return true.

      • Symmetric: For any non-null reference values x and y , x.equals(y) must return true if and only if y.equals(x) returns true.

      • Transitive: For any non-null reference values x , y , z , if x.equals(y) returns true and y.equals(z) returns true , then x.equals(z) must return true.

      • Consistent: For any non-null reference values x and y , multiple invocations of x.equals(y) must consistently return true or consistently return false, provided no information used in equals comparisons is modified.

      • For any non-null reference value x , x.equals(null) must return false.

  11. Always override hashcode when you override equals

    • Summary - We must override hashCode every time you override equals , or your program will not run correctly. Your hashCode method must obey the general contract specified in Object and must do a reasonable job assigning unequal hash codes to unequal instances. This is easy to achieve, if slightly tedious, using the recipe on page 51. As mentioned in Item 10, the AutoValue framework provides a fine alternative to writing equals and hashCode methods manually, and IDEs also provide some of this functionality.

      • We must override hashCode in every class that overrides equals.

      • The key provision that is violated when you fail to override hashCode is the second one: equal objects must have equal hash codes.

      • Do not be tempted to exclude significant fields from the hash code computation to improve performance.

      • 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.

  12. Always override toString

    • Summary - While Object provides an implementation of the toString method, the string that it returns is generally not what the user of your class wants to see. It consists of the class name followed by an “at” sign ( @ ) and the unsigned hexadecimal representation of the hash code, for example, PhoneNumber@163b91 . 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.” While it could be argued that PhoneNumber@163b91 is concise and easy to read, it isn’t very informative when compared to 707-867-5309 . The toString contract goes on to say, “It is recommended that all subclasses override this method.” Good advice, indeed!

      • Providing a good toString implementation makes your class much more pleasant to use and makes systems using the class easier to debug.

      • When practical, the toString method should return all of the interesting information contained in the object

      • Whether or not you decide to specify the format, you should clearly document your intentions.

      • Provide programmatic access to the information contained in the value returned by toString.

  13. Override clone judiciously

    • Summary - New interfaces should not extend it, and new extendable classes should not implement it. While it’s less harmful for final classes to implement Cloneable , this should be viewed as a performance optimization, reserved for the rare cases where it is justified. As a rule, copy functionality is best provided by constructors or factories. A notable exception to this rule is arrays, which are best copied with the clone method.

      • In practice, a class implementing Cloneable is expected to provide a properly functioning public clone method.

      • Immutable classes should never provide a clone method

      • In effect, the clone method functions as a constructor; you must ensure that it does no harm to the original object and that it properly establishes invariants on the clone.

      • The Cloneable architecture is incompatible with normal use of final fields referring to mutable objects

      • Public clone methods should omit the throws clause

      • A better approach to object copying is to provide a copy constructor or copy factory.

  14. Consider implementing Comparable

    • Summary - whenever we implement a value class that has a sensible ordering, we 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.

      • Use of the relational operators < and > in compareTo methods is verbose and error-prone and no longer recommended.
  15. Minimize the accessibility of classes and members

    • Summary - We should reduce accessibility of program elements as much as possible (within reason). After carefully designing a minimal public API, we should prevent any stray classes, interfaces, or members from becoming part of the API. With the exception of public static final fields, which serve as constants, public classes should have no public fields. Ensure that objects referenced by public static final fields are immutable.

      • Make each class or member as inaccessible as possible.

      • Instance fields of public classes should rarely be public.

      • Classes with public mutable fields are not generally thread-safe.

      • It is wrong for a class to have a public static final array field, or an accessor that returns such a field.

  16. In public classes, use accessor methods, not public fields

    • Summary - Public classes should never expose mutable fields. It is less harmful, though still questionable, for public classes to expose immutable fields. It is, however, sometimes desirable for package-private or private nested classes to expose fields, whether mutable or immutable.

      • If a class is accessible outside its package, provide accessor methods.

      • If a class is package-private or is a private nested class, there is nothing inherently wrong with exposing its data fields.

  17. Minimize mutability

    • Summary - Resist the urge to write a setter for every getter. Classes should be immutable unless there’s a very good reason to make them mutable. Immutable classes provide many advantages, and their only disadvantage is the potential for performance problems under certain circumstances. We should always make small value objects. (There are several classes in the Java platform libraries, such as java.util.Date and java.awt.Point, that should have been immutable but aren’t.) We should seriously consider making larger value objects, such as String and BigInteger, immutable as well. We should provide a public mutable companion class for our immutable class only once we’ve confirmed that it’s necessary to achieve satisfactory performance.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.
    • Advantages -

      • Immutable objects are simple.
      • Immutable objects are inherently thread-safe; they require no synchronization.
      • Immutable objects can be shared freely.
      • 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.
    • Disadvantages -

      • Immutable classes require a separate object for each distinct value.
  18. Favor composition over inheritance

    • Summary - 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.

      • Unlike method invocation, inheritance violates encapsulation.
  19. Design and document for inheritance or else prohibit it

    • Summary - Designing a class for inheritance is hard work. We must document all of its self-use patterns, and once we’ve documented them, we must commit to them for the life of the class. If we fail to do this, subclasses may become dependent on implementation details of the superclass and may break if the implementation of the superclass changes. To allow others to write efficient subclasses, we may also have to export one or more protected methods. Unless we know there is a real need for subclasses, we are probably better off prohibiting inheritance by declaring our class final or ensuring that there are no accessible constructors.

      • The class must document its self-use of overridable methods.
      • A class may have to provide hooks into its internal workings in the form of judiciously chosen protected methods
      • The only way to test a class designed for inheritance is to write subclasses.
      • We must test our class by writing subclasses before we release it.
      • Constructors must not invoke overridable methods, directly or indirectly
      • Neither clone nor readObject may invoke an overridable method, directly or indirectly.
      • Designing a class for inheritance requires great effort and places substantial limitations on the class.
      • The best solution to this problem is to prohibit subclassing in classes that are not designed and documented to be safely subclassed.
  20. Prefer interfaces to abstract classes

    • Summary - An interface is generally the best way to define a type that permits multiple implementations. If we export a nontrivial interface, we should strongly consider providing a skeletal implementation to go with it. To the extent possible, we should provide the skeletal implementation via default methods on the interface so that all implementors of the interface can make use of it. That said, restrictions on interfaces typically mandate that a skeletal implementation take the form of an abstract class.

      • Existing classes can easily be retrofitted to implement a new interface.
      • Interfaces are ideal for defining mixins.
      • Interfaces allow for the construction of nonhierarchical type frameworks.
      • Interfaces enable safe, powerful functionality enhancements via the wrapper class idiom
      • Good documentation is absolutely essential in a skeletal implementation.
  21. Design interfaces for posterity

    • It is not always possible to write a default method that maintains all invariants of every conceivable implementation.
    • In the presence of default methods, existing implementations of an interface may compile without error or warning but fail at runtime.
    • Even though default methods are now a part of the Java platform, it is still of the utmost importance to design interfaces with great care.
    • While it may be possible to correct some interface flaws after an interface is released, you cannot count on it.
  22. Use interfaces only to define types

    • Summary - Interfaces should be used only to define types. They should not be used merely to export constants.

      • The constant interface pattern is a poor use of interfaces.
  23. Prefer class hierarchies to tagged classes

    • Summary - Tagged classes are seldom appropriate. If we’re tempted to write a class with an explicit tag field, think about whether the tag could be eliminated and the class replaced by a hierarchy. When we encounter an existing class with a tag field, consider refactoring it into a hierarchy.

      • Tagged classes are verbose, error-prone, and inefficient.
      • A tagged class is just a pallid imitation of a class hierarchy.
  24. Favor static member classes over nonstatic

    • Summary - There are four different kinds of nested classes, and each has its place. If a nested class needs to be visible outside of a single method or is too long to fit comfortably inside a method, use a member class. If each instance of a member class needs a reference to its enclosing instance, make it nonstatic; otherwise, make it static. Assuming the class belongs inside a method, if you need to create instances from only one location and there is a preexisting type that characterizes the class, make it an anonymous class; otherwise, make it a local class.

      • If we 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.
  25. Limit source files to a single top-level class

    • Summary - Never put multiple top-level classes or interfaces in a single source file. Following this rule guarantees that we can’t have multiple definitions for a single class at compile time. This in turn guarantees that the class files generated by compilation, and the behavior of the resulting program, are independent of the order in which the source files are passed to the compiler.
  26. Don’t use raw types

    • Summary - Using raw types can lead to exceptions at runtime, so don’t use them. They are provided only for compatibility and interoperability with legacy code that predates the introduction of generics. As a quick review, Set<Object> is a parameterized type representing a set that can contain objects of any type, Set<?> is a wildcard type representing a set that can contain only objects of some unknown type, and Set is a raw type, which opts out of the generic type system. The first two are safe, and the last is not.

      • If we use raw types, we lose all the safety and expressiveness benefits of generics.
      • We lose type safety if we use a raw type such as List , but not if we use a parameterized type such as List<Object>.
      • We can’t put any element (other than null ) into a Collection<?>
      • We must use raw types in class literals.
  27. Eliminate unchecked warnings

    • Summary - Unchecked warnings are important. Don’t ignore them. Every unchecked warning represents the potential for a ClassCastException at runtime. We should do our best to eliminate these warnings. If we can’t eliminate an unchecked warning and we can prove that the code that provoked it is typesafe, suppress the warning with a @SuppressWarnings("unchecked") annotation in the narrowest possible scope. Record the rationale for our decision to suppress the warning in a comment.

      • Eliminate every unchecked warning that we can.
      • If we can’t eliminate a warning, but we can prove that the code that provoked the warning is typesafe, then (and only then) suppress the warning with an @SuppressWarnings("unchecked") annotation.
      • Always use the SuppressWarnings annotation on the smallest scope possible.
      • Every time we use a @SuppressWarnings("unchecked") annotation, add a comment saying why it is safe to do so.
  28. Prefer lists to arrays

    • Summary - 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 we find ourself mixing them and getting compile-time errors or warnings, our first impulse should be to replace the arrays with lists.
  29. Favor generic types

    • Summary - Generic types are safer and easier to use than types that require casts in client code. When we design new types, make sure that they can be used without such casts. This will often mean making the types generic. If we have any existing types that should be generic but aren’t, generify them. This will make life easier for new users of these types without breaking existing clients.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment