Item 1: Consider static
factory methods instead of Constructors
- A class can provide its clients with static factory methods instead of, or in addition to, public constructors. Providing a static factory method instead of a public
constructor has both advantages and disadvantages.
- One advantage of static factory methods is that, unlike constructors, they have names. A class can have only a single constructor with a given signature. Programmers
have been known to get around this restriction by providing two constructors whose parameter lists differ only in the order of their parameter types. This is a bad
idea. Because they have names, static factory methods don't share the same restriction.
- A second advantage of static factory methods is that, unlike constructors, they are not required to create a new object each time they're invoked. This allows
immutable classes (Item 17) to use preconstructed instances, or to cache instances as they're constructed, and dispense them repeatedly to avoid creating unnecessary
duplicate objects.
- The ability of static factory methods to return the same object from repeated invocations allows classes to maintain strict control over what instances exist at any
time. Classes that do this are said to be
instance-controlled. Instance control allows a class to guarantee that it is a singleton (Item 3) or noninstantiable
(Item 4). Also, it allows an immutable value class (Item 17) to make the guarantee that no two equal instances exist:
a.equals(b)
if and only if
a == b
. This is the basis of the Flyweight pattern.
- For instance consider the following code. When a new instance of the
CreatorClass is being created, the first thing that will be done is that the
static
variable,
baseClass will be initialized. Now everytime that the
getBaseClassInstance method is called to return an instance of the
BaseClass object, it will the
same instance of
BaseClass that will be returned. This is what we are validating by using the
==
check
in the code as well.
Expand Gist
- A third advantage of static factory methods is that, unlike constructors, they can return an object of any subtype of their return type. This gives you great
flexibility in choosing the class of the returned object. Look at the Pizza example from Item#2 to see an example of this in action.
- A fourth advantage of static factories is that the class of the returned object can vary from call to call as a function of the input parameters. Any subtype of the
declared return type is permissible.
- A fifth advantage of static factories is that the class of the returned object need not exist when the class containing the method is written. Such flexible static
factory methods form the basis of service provider frameworks, like the Java Database Connectivity API (JDBC). A service provider framework is a system in which
providers implement a service, and the system makes the implementations available to clients, decoupling the clients from the implementations.
- The main limitation of providing only static factory methods is that classes without public or protected constructors cannot be subclassed.
- A second shortcoming of static factory methods is that they are hard for programmers to find. They do not stand out in API documentation in the way that constructors
do, so it can be difficult to figure out how to instantiate a class that provides static factory methods instead of constructors.
Some common names for static
methods
:
a)
from
: A
type-conversion method that takes a single parameter and returns a corresponding instance of this type, for example:
Date d = Date.from(instant);
b)
of
: An aggregation method that takes multiple parameters and returns an instance of this type, for example:
Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);
c)
valueOf
: A more verbose version alternative to
from
and
of
, for example:
BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
d)
instance
or
getInstance
: Returns an instance that is described by its parameters (if any) but cannot be said to have the same value, for
example:
StackWalker luke = StackWalker.getInstance(options);
e)
create
or
newInstance
: Like
instance
or
getInstance
, except that the method guarantees that each call returns a
new instance, for example:
Object newArray = Array.newInstance(classObject, arrayLen);
f)
getType
: Like
getInstance
, but used if the factory method is in a different class.
Type is the type of object returned by the
factory method, for example:
FileStore fs = Files.getFileStore(path);
g)
newType
: Like
newInstance
but used if the factory method is in a different class.
Type is the type of object returned by the
factory method, for example:
BufferedReader br = Files.newBufferedReader(path);
h)
type
: A concise alternative to getType and newType, for example:
List<Complaint> litany = Collections.list(legacyLitany);
How to use the Builder pattern where inheritance is involved?
- This seems to be quite the struggle in itself. Google: "use inheritance in builder pattern java". // TODO: Some links:
a)
Builder Pattern and Inheritance
b)
Subclassing a Java Builder class
Item 2: Consider a Builder
when faced with many constructor parameters
- Static factories and constructors share a limitation: they do not scale well to large numbers of optional parameters. Consider the case of a class representing the
Nutrition Facts label that appears on packaged foods. These labels have a few required fields - serving size, servings per container, and calories per serving - and
more than twenty optional fields - total fat, saturated fat, trans fat, cholesterol, sodium, and so on.
- Traditionally, programmers have used the
telescoping constructor pattern, in which you provide a constructor with only the required parameters, another with a
single optional parameter, a third with two optional parameters, and so on, culminating in a constructor with all the optional parameters. The telescoping constructor
pattern works, but it is hard to write client code when there are many parameters, and harder still to read it.
- A second alternative when you're faced with many optional parameters in a constructor is the
JavaBeans pattern, in which you call a parameterless
constructor to create the object and then call setter methods to set each required parameter and each optional parameter of interest.
- Unfortunately, the JavaBeans pattern has serious disadvantages of its own. Because construction is split across multiple calls, a JavaBean may be in an inconsistent
state partway through its construction. The class does not have the option of enforcing consistency merely by checking the validity of the constructor parameters.
Attempting to use an object when it's in an inconsistent state may cause failures that are far removed from the code containing the bug and hence difficult to debug. A
related disadvantage is that the JavaBeans pattern precludes (prevent from happening; make impossible) the possibility of making a class immutable (Item 17) and
requires added effort on the part of the programmer to ensure thread safety.
- It is possible to reduce these disadvantages by manually "freezing" the object when its construction is complete and not allowing it to be used until frozen, but this
variant is unwieldy and rarely used in practice. What does "freezing" an object in Java mean? Read
here on SE. This is a messy pattern and probably not widely used. A far neater pattern is
the builder pattern.
What is 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.
- IntelliJ can create Builder Patterns for you
as shown here on
SO.
Expand Gist
- The Builder pattern is well-suited to class hierarchies. The following example makes use of recursive type parameters. Read more about them
here on the other page.
- You can refactor the existing constructor in your class into a Builder or a static method by right-clicking on the constructor - Refactor - Replace Constructor With *
- There is stuff going on over here that I do not understand.
// TODO: Builder pattern for class hierarchies
- The technique, wherein a subclass method is declared to return a subtype of the return type declared in the superclass, is known as
covariant return typing. It allows clients to use these builders without the need for casting.
- Note that Pizza.Builder is a generic type with a recursive type parameter (Item 30). This, along with the abstract self method, allows method chaining to work
properly in subclasses, without the need for casts.
Expand Gist
Item 3: Enforce the Singleton property with a private constructor or an Enum type
- The first two techniques of creating a Singleton is by creating a class with a
private
constructor.
- Read
this explanation on DZone explaining how to use Enums for creating Singletons. It also goes
over the first two techniques of creating singletons along with their drawbacks.
Expand Gist
- Go through all the answers at
this SO Link.
- An enum is a java class with the special condition that its definition must start with at least one "enum constant". Apart from that, and that enums can't be
extended or used to extend other classes, an enum is a class like any class and you use it by adding methods below the constant definitions:
Expand Gist
- Note that the enum is created only when the INSTANCE field is first accessed. Only at that point will JVM implicitly call the (implicit) constructor of the enum.
Hence the instantiation is a lazy instantiation. You can override the constructor of the enum that you create. But note that manually calling the constructor is a
compile error. To sum up, by default enums have implicit private constructor and that explicitly adding a private constructor is not needed unless you actually have
code that you need to run in that constructor
- Why is it mentioned in the book that a Singleton Class is hard to test. "Making a class a singleton can make it difficult to test its clients because it's impossible
to substitute a mock implementation for a singleton unless it implements an interface that serves as its type." Read
this SO Link.
What is the difference between a Singleton class and a static class? Should a Java singleton use static
fields? When should a
Singleton be used, and when should a static
static class be used?
-
Difference between static class and singleton pattern?
-
Should a Java singleton use static variables?
-
When to use a Singleton and when to
use a static class?
Item 4: Enforce non-instantiability with a private constructor
- You have the concept of
Utility Classes in java. The idea is that these classes have
static
methods that serve some specific purpose, but the
class in which the methods are present is NEVER meant to be instantiated. For example, consider the
java.lang.Math
class. You can call a number of methods
from this class like
Math.log10(1000)
,
Math.pow(10,2)
etc. All of these methods are declared
static
in the
Math
class and you are not expected to instantiate the
Math
class in order to make use of these methods. In fact, you CANNOT instantiate the
Math
class because the constructor of the
Math
class is declared
private
with the following comment on top of it:
Don't let anyone instantiate
this class..
- You can see the same thing when you look at the
java.util.Arrays
class as well. Remember this class has useful methods such as
Arrays.asList("Alice", "Bob");
. Again, just like the
Math
class, these methods are also declared
static
. And the
Arrays
class itself has a
private
constructor. With the following comment:
Suppresses default constructor, ensuring non-instantiability.
- This is important. This idiom is mildly counterintuitive because the
private
constructor is provided expressly so that it cannot be invoked. Remember
that if you did not specify a constructor, then java would have provided a default constructor, which would have allowed others to instantiate the class.
- Attempting to enforce noninstantiability by making a class
abstract
does not work. The class can be subclassed and the subclass instantiated.
- Note that as a side-effect of using this idiom, this prevents the Utility Class from being subclassed. All constructors must invoke a superclass constructor,
explicitly or implicitly, and a subclass would have no accessible superclass constructor to invoke.
Expand Gist
Item 5: Prefer Dependency Injection to hard-wiring resources
- Static utility classes and singletons are inappropriate for classes whose behavior is parameterized by an underlying resource.
- Imagine you wanted to create a spell checker. The spell checker depends on the dictionary that is going to be used for testing the spelling. Thus the dictionary is a
dependency for the
SpellChecker class. You can create an instance variable of type
Dictionary in the
SpellChecker class and have that class use
that particular dictionary. But now if you want to create a spell checker for a different language, you will have to create a different
SpellChecker class with a
different
Dictionary. This is certainly not ideal.
Expand Gist
- What is required is the ability to support multiple instances of the class (in our example,
SpellChecker), each of which uses the resource desired by the
client (in our example, the dictionary). 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: the dictionary is a
dependency of the spell checker and is
injected into the spell checker when it is
created.
Expand Gist
- Read about
how DI improves the testability of class here on SO.
Item 6: Avoid creating unnecessary objects
- Never use
new String()
to create a new
String
. Use the String literal because in that way you can reuse the same String instance.
- Why does the code say: "Prevents VM from optimizing away everything". Explained
here on SO.
Expand Gist
- You can often avoid creating unnecessary objects by using static factory methods (Item 1) in preference to constructors on immutable classes that provide both.
- Use a
Pattern
when you are trying to match a string with a regex instead of directly calling
yourStr.matches("regexExpression")
. Note the
below example how we are making use of the
Pattern
class.
- 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. The problem is that it internally creates a
Pattern
instance for the regular expression and uses it only once, after which it becomes eligible
for garbage collection. Creating a
Pattern
instance is expensive because it requires compiling the regular expression into a finite state machine. To
improve the performance, explicitly compile the regular expression into a
Pattern
instance (which is immutable) as part of class initialization, cache it,
and reuse the same instance for every invocation of the
isRomanNumeral method.
Expand Gist
- You can use the
keySet
method of a
Map
interface to get a view of the keys of the
Map
in the form of a
Set
. The
keySet
is backed by the underlying
Map
, i.e. any changes to the key set are going to be reflected in the
Map
. If you create
multiple instances of a key set from the same
Map
, making changes to one key set are going to impact all of the other key sets that you have created as
well.
- Prefer primitives to boxed primitives, and be wary of unintentional autoboxing. In the below example, the variable
sum is declared as a
Long
instead of a
long
, which means that the program constructs about 2^31 unnecessary
Long
instances (roughly one for each time the
long
i is added to the
Long
sum). Changing the declaration of
sum from
Long
to
long
reduces the runtime.
Expand Gist
Item 7: Eliminate obsolete object references
- You can run into
OutOfMemoryError
in Java as well if you are not careful with Objects that are no longer in use in the program, but your
datastructures are still maintaining a reference to it.
- Consider the below code. As you are adding elements to the array, you are increasing the
length value, and once you "remove" the last element, you are
simulating that by just reducing the
length parameter of the array. This causes a memory leak, because your array is still storing a reference to the object in
the array. Imagine this was a reference to a rather large object, which had references of its own. As your program scaled, you would have run into memory issues.
- One way of getting around this problem is by
null
ing out the references once you are done with them.
Expand Gist
- Nulling out object references should be the exception rather than the norm. The best way to eliminate an obsolete reference is to let the variable that contained the
reference fall out of scope. This occurs naturally if you define each variable in the narrowest possible scope.
-
So when should you null out a reference? What aspect of the MyArray class makes it susceptible to memory leaks?
- Simply put, it manages its own memory.
- The storage pool consists of the elements of the
myArray array (the object reference cells, not the objects themselves). The elements in the active portion of
the array (elements of the array that are at index less than
length) are allocated, and those in the remainder of the array are free. The garbage collector has
no way of knowing this; to the garbage collector, all of the object references in the elements array are equally valid. Only the programmer knows that the inactive
portion of the array is unimportant. The programmer effectively communicates this fact to the garbage collector by manually nulling out array elements as soon as they
become part of the inactive portion. Generally speaking, whenever a class manages its own memory, the programmer should be alert for memory leaks. Whenever an element
is freed, any object references contained in the element should be nulled out.
Item 8: Avoid finalizers and cleaners
- Finalizers are deprecated as of Java 9 and have been replaced by cleaners. Cleaners are less dangerous than finalizers, but still unpredictable, slow, and generally
unnecessary.
-
This SO answer goes into details about why the mechanism of
finalization is weird?
-
This SO answer goes into details about Cleaners vs Finalizers.
- Refer text for complete explanation.
Item 9: Prefer try-with resources to try-finally
- Always use try-with-resources instead of using the old try-catch block.
- The problem with using the older method is: Even the correct code for closing resources with try-finally statements, as illustrated in the first code example, has a
subtle deficiency. The code in both the try block and the finally block is capable of throwing exceptions. For example, in the firstLineOfFile method, the call to
readLine could throw an
exception due to a failure in the underlying physical device, and the call to close
could then fail for the same reason.
Expand Gist
- Although
Object
is a concrete class, it is designed primarily for extension. All of its non-final methods (
equals
,
hashCode
,
toString
,
clone
, and
finalize
) have explicit
general contracts because they are designed to be overridden. It is the
responsibility of any class overriding these methods to obey their general contracts; failure to do so will prevent other classes that depend on the contracts (such as
HashMap
and
HashSet
) from functioning properly in conjunction with the class.
- The equals method for class
Object
implements the most discriminating possible equivalence relation on objects; that is, for any non-null reference values x
and y, this method returns true if and only if x and y refer to the same object (x == y has the value true).
- This chapter tells you when and how to override the nonfinal
Object
methods.
Item 10: Obey the general contract when overriding equals
- You can read about the equals and hashcode
here on Baeldung.
- And
here are on Thorben Janssen
-
SO Link on the topic
- The easiest way to avoid problems when overriding
equals
is to
not override
equals
. This is the right thing to do if any of the
following conditions apply:
a) Each instance of the class is inherently unique: This is the case when a class represents active
entities rather than
values.
b) There is no need for the class to provide a "logical equality" test: Does the client need the functionality where they will have to compare two instances of this
class?
c) A superclass has already overridden
equals, and the superclass behavior is appropriate for this class: For example, most
Set
implementations
inherit their
equals
implementation from
AbstractSet
,
List
implementations from
AbstractList
, and
Map
implementations from
AbstractMap
.
d) The class is
private
or
package-private
and you are sure that its
equals
method will never be invoked.
So when is it appropriate to override equals
?
- It is when a class has a notion of
logical equality that differs from mere
object identity and a superclass has not already overridden
equals
. This is generally the case for
value classes. A value class is simply a class that represents a value, such as
Integer
or
String
.
- Someone that calls
equals
on such a class will expect the result to return whether the two classes are logically equivalent, and not just whether they
refer to the same object or not. Overriding
equals
is necessary to satisfy this expectation, as well as enable the instances of these value classes to
serve as keys to a map, or allow them to be added to a Set.
- Note that singleton value classes do not need their
equals
method overridden, since there will always be just a single instance of these classes. Hence
in these cases the object equality is the same as the value equality.
What is the contract of the equals
method?
- The contract says that the
equals
method should be an equivalence relation. What this means is that the
equals
method should satisfy the
following conditions:
a)
Reflexive
: Each instance should be equal to itself. For any non-null reference value x,
x.equals(x)
must return true. If
you were to violate it and then
add an instance of your class to a collection, the
contains
method might well say that the collection didn't contain the instance that you just added. Hard
to screw this one up.
b)
Symmetry
: For any non-null reference values x and y,
x.equals(y)
must return true if and only if
y.equals(x)
returns true.
- Consider this implementation of
equals
. As expected,
cis.equals(s)
returns true. The problem is that while the equals method in
CaseInsensitiveString knows about ordinary strings, the
equals
method in
String
is oblivious to case-insensitive strings (i.e. no
relations has been specified between the
CaseInsensitiveString class and the
String
class). Therefore,
s.equals(cis)
returns false, a
clear violation of symmetry.
Expand Gist
- In order to correct this error, we change the
equals
implementation as follows. Now, we are no longer trying to compare
String
and
CaseInsensitiveString
in the
equals
method. Hence, comparison on both sides now returns
false.
Expand Gist
- This is the equals that was auto-generated by IntelliJ. Note that the overridden equals methods is checking whether the two strings being compared are EXACTLY equal
or not. And not checking for equalsIgnoreCase, as we want. So do not blindly use the auto-generated equals method. Check what is going on.
Expand Gist
c)
Transitivity
: 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.
- Consider the case of a subclass that adds a new
value component to its superclass. Below is a class
Point that is being extended by
ColorPoint.
ColorPoint adds a new field called
color to the
Point class.
Expand Gist
- Now, how should the
equals
of the
ColorPoint class be written? If you leave it out entirely, the implementation is inherited from Point and color
information is ignored in equals comparisons. While this does not violate the equals contract, it is clearly unacceptable.
- So the first version that we write compares whether the points are the same and the color is the same as well. But this solution breaks the rule of symmetricity.
point.equals(colorPoint))
returns true, whereas
colorPoint.equals(point)
returns false.
- Note that the expectation here is that
both of the comparisons should return
false.
Expand Gist
- So, in order to get around this issue, say we decide that if the types are different, then we are not going to compare the
color values at all. And that kind
of makes sense as well. But if we do this, this ends up breaking the rule of transitivity.
Expand Gist
So, what's the solution?
- It turns out that this is a fundamental problem of equivalence relations in object-oriented languages.
- There is no way to extend an instantiable class and add a value component while preserving the equals contract, unless you're willing to forgo the benefits of
object-oriented abstraction.
- You may hear it said that you can extend an instantiable class and add a value component while preserving the equals contract by using a
getClass
test in
place of the
instanceof
test in the
equals
method. In fact, note that this is what Intellij was auto-generating in the example above. This is
an example of using this
equals
in the
Point class. We already saw that this will return false when performing the comparisons from both sides.
- This has the effect of equating objects only if they have the same implementation class. This may not seem so bad, but the consequences are unacceptable: An instance
of a subclass of
Point is still a
Point, and it still needs to function as one, but it fails to do so if you take this approach!
- You can read about the
Liskov Substitution Principle here on the other page.
- In the below example, although the
ColorPoint class extends the
Point class (and hence every instance of a
ColorPoint class IS-A
Point
class), the
equals
method of the
Point class checks for the exact implementation type in
getClass
. Hence the code treats the two
classes as different classes and that is a violation of Liskov Substitution Principle.
Expand Gist
What is a workaround to this problem then?
- Follow the advice of Item 18, "Favor composition over inheritance."
- Instead of having
ColorPoint extend
Point, give
ColorPoint a private
Point field and a public view method (Item 6) that returns the point
at the same position as this color point:
Expand Gist
- Note that you can add a value component to a subclass of an abstract class without violating the equals contract. (Item 23)
d)
Consistency
: 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.
- If two objects are equal, they must remain equal for all time unless one (or both) of them is modified. In other words, mutable objects can be equal to different
objects at different times while immutable objects can't.
- Do not write an equals method that depends on unreliable resources. equals methods should perform only deterministic computations on memory-resident objects.
e)
Non-Nullity
: For any non-null reference value x,
x.equals(null)
must return false.
- Note the places above where we have been using the
instanceof
operator.
- To test its argument for equality, the equals method must first cast its argument to an appropriate type so its accessors can be invoked or its fields accessed.
Before doing the cast, the method must use the
instanceof
operator to check that its argument is of the correct type.
- If this type check were missing and the
equals
method were passed an argument of the wrong type, the
equals
method would throw a
ClassCastException
, which violates the equals contract. But the
instanceof
operator is specified to return false if its first operand is null,
regardless of what type appears in the second operand. Therefore, the type check will return false if null is passed in, so you don't need an explicit null check.
Expand Gist
What are the things to remember when writing a custom equals
method
?
Expand Gist
Item 11: Always override hashCode
when you override equals
- The contract of the
hashCode
states the following three rules:
a) When the
hashCode
method is invoked on an object repeatedly during an execution of an application, it must consistently return the same value, provided
no information used in equals comparisons is modified. This value need not remain consistent from one execution of an application to another.
b) If two objects are equal according to the
equals(Object)
method, then calling
hashCode
on the two objects must produce the same integer
result.
c) If two objects are unequal according to the
equals(Object)
method, it is
not required that calling
hashCode
on each of the objects
must produce distinct results. However, the programmer should be aware that producing distinct results for unequal objects may improve the performance of hash tables.
- The key provision that is violated when you override the
equals
but do not override the
hashCode
is point 2: equal objects
must have
equal hash codes. If you override
equals
but do not override
hashCode
, two distinct instances of a class might be
logically equal
according to the
equals
method that you overrode. But these two equal objects will end up returning different numbers when you call the
hashCode
method on these two logically equal objects.
- So why does this matter? Consider the below code.
- The PhoneNumber class's failure to override
hashCode
causes the two equal instances to have unequal hash codes, in violation of the hashCode contract.
Therefore, the
get
method is likely to look for the phone number in a different hash bucket from the one in which it was stored by the
put
method. Even if the two instances happen to hash to the same bucket, the
get
method will almost certainly return
null
, because
HashMap
has an optimization that caches the hash code associated with each entry and doesn't bother checking for object equality if the hash codes don't
match.
Expand Gist
So how should we write a proper hashCode
function?
- Read the complete thing in the text.
-
What is a "canonical
representation" of a field mean here on SO.
- You may exclude
derived fields from the hash code computation. In other words, you may ignore any field whose value can be computed from fields included
in the computation.
- You
must exclude any fields that are not used in
equals
comparisons, or you risk violating the second provision of the
hashCode
contract.
- This is what the auto-generated
hashCode
from IntelliJ looks like. Note that in this auto-generated method, we are not calculating the hash values of the
individual fields for some reason.
Expand Gist
- Alternatively, the
Objects
class has a static
hash
method that takes an arbitrary number of objects and returns a hash code for them. This
method lets you write one-line
hashCode
methods whose quality is comparable to those written according to the recipe in this item. Unfortunately, they run
more slowly because they entail array creation to pass a variable number of arguments, as well as boxing and unboxing if any of the arguments are of primitive type.
This style of hash function is recommended for use only in situations where performance is not critical.
- This is what it looks like.
Expand Gist
- If a class is immutable and the cost of computing the hash code is significant, you might consider caching the hash code in the object rather than recalculating it
each time it is requested. If you believe that most objects of this type will be used as hash keys, then you should calculate the hash code when the instance is
created. Otherwise, you might choose to lazily initialize the hash code the first time hashCode is invoked. The
String
class for example lazily-initializes
it's hash code. Once the hashCode has been computed, it caches the value.
Expand Gist
- Do not be tempted to exclude significant fields from the hash code computation to improve performance. While the resulting hash function may run faster, its poor
quality may degrade hash tables' performance to the point where they become unusable. In particular, the hash function may be confronted with a large collection of
instances that differ mainly in regions you've chosen to ignore.
Item 12: Always override toString
- 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.
- Override Object
's toString
implementation in every instantiable class you write, unless a superclass has already done so. It makes classes
much more pleasant to use and aids in debugging. The toString
method should return a concise, useful description of the object, in an aesthetically
pleasing format.
Item 13: Override clone
judiciously
- Read text.
- The idea is to create a deep-copy of the object. But you cannot have your class just implement the
Cloneable
interface in order to achieve this
functionality. Your class needs to, both, implement
Cloneable
and override the
clone
method from the
Object
class in order to
achieve a deep copy.
- But there are better ways to create a copy of the object. Either by using a copy constructor, or by using a static method.
clone
is rarely used to
achieve this outcome. This is also the way that is preferred nowadays? See for example SO answer
here and
here.
- If we can create a deep copy of an object by specifying a method of our own, why do we need to explicitly implement and then override the
clone
method?
Explained here on SO.
Item 14: Consider implementing Comparable
- A lot of stuff going on over here. Read the text for full info.
- If you are writing a value class with an obvious natural ordering, such as alphabetical order, numerical order, or chronological order, you should implement the
Comparable
interface. Implementing the Comparable
interface means overriding the compareTo
method.
- The general contract of the compareTo
method is similar to that of equals
:
Compares this object with the specified object for order. Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater
than the specified object. Throws ClassCastException
if the specified object's type prevents it from being compared to this object.