Effective Java Review (90 Part Series)
1 Effective Java Tuesday! Let’s Consider Static Factory Methods
2 Effective Java Tuesday! The Builder Pattern!
… 86 more parts…
3 Effective Java Tuesday! Singletons!
4 Effective Java Tuesday! Utility Classes!
5 Effective Java Tuesday! Prefer Dependency Injection!
6 Effective Java Tuesday! Avoid Creating Unnecessary Objects!
7 Effective Java Tuesday! Don’t Leak Object References!
8 Effective Java Tuesday! Avoid Finalizers and Cleaners!
9 Effective Java Tuesday! Prefer try-with-resources
10 Effective Java Tuesday! Obey the `equals` contract
11 Effective Java Tuesday! Obey the `hashCode` contract
12 Effective Java Tuesday! Override `toString`
13 Effective Java Tuesday! Override `clone` judiciously
14 Effective Java Tuesday! Consider Implementing `Comparable`
15 Effective Java Tuesday! Minimize the Accessibility of Classes and Member
16 Effective Java Tuesday! In Public Classes, Use Accessors, Not Public Fields
17 Effective Java Tuesday! Minimize Mutability
18 Effective Java Tuesday! Favor Composition Over Inheritance
19 Effective Java Tuesday! Design and Document Classes for Inheritance or Else Prohibit It.
20 Effective Java Tuesday! Prefer Interfaces to Abstract Classes
21 Effective Java! Design Interfaces for Posterity
22 Effective Java! Use Interfaces Only to Define Types
23 Effective Java! Prefer Class Hierarchies to Tagged Classes
24 Effective Java! Favor Static Members Classes over Non-Static
25 Effective Java! Limit Source Files to a Single Top-Level Class
26 Effective Java! Don’t Use Raw Types
27 Effective Java! Eliminate Unchecked Warnings
28 Effective Java! Prefer Lists to Array
29 Effective Java! Favor Generic Types
30 Effective Java! Favor Generic Methods
31 Effective Java! Use Bounded Wildcards to Increase API Flexibility
32 Effective Java! Combine Generics and Varargs Judiciously
33 Effective Java! Consider Typesafe Heterogenous Containers
34 Effective Java! Use Enums Instead of int Constants
35 Effective Java! Use Instance Fields Instead of Ordinals
36 Effective Java! Use EnumSet Instead of Bit Fields
37 Effective Java! Use EnumMap instead of Ordinal Indexing
38 Effective Java! Emulate Extensible Enums With Interfaces.
39 Effective Java! Prefer Annotations to Naming Patterns
40 Effective Java! Consistently Use the Override Annotation
41 Effective Java! Use Marker Interfaces to Define Types
42 Effective Java! Prefer Lambdas to Anonymous Classes
43 Effective Java! Prefer Method References to Lambdas
44 Effective Java! Favor the Use of Standard Functional Interfaces
45 Effective Java! Use Stream Judiciously
46 Effective Java! Prefer Side-Effect-Free Functions in Streams
47 Effective Java! Prefer Collection To Stream as a Return Type
48 Effective Java! Use Caution When Making Streams Parallel
49 Effective Java! Check Parameters for Validity
50 Effective Java! Make Defensive Copies When Necessary
51 Effective Java! Design Method Signatures Carefully
52 Effective Java! Use Overloading Judiciously
53 Effective Java! Use Varargs Judiciously
54 Effective Java! Return Empty Collections or Arrays, Not Nulls
55 Effective Java! Return Optionals Judiciously
56 Effective Java: Write Doc Comments For All Exposed APIs
57 Effective Java: Minimize The Scope of Local Variables
58 Effective Java: Prefer for-each loops to traditional for loops
59 Effective Java: Know and Use the Libraries
60 Effective Java: Avoid Float and Double If Exact Answers Are Required
61 Effective Java: Prefer Primitive Types to Boxed Types
62 Effective Java: Avoid Strings When Other Types Are More Appropriate
63 Effective Java: Beware the Performance of String Concatenation
64 Effective Java: Refer to Objects By Their Interfaces
65 Effective Java: Prefer Interfaces To Reflection
66 Effective Java: Use Native Methods Judiciously
67 Effective Java: Optimize Judiciously
68 Effective Java: Adhere to Generally Accepted Naming Conventions
69 Effective Java: Use Exceptions for Only Exceptional Circumstances
70 Effective Java: Use Checked Exceptions for Recoverable Conditions
71 Effective Java: Avoid Unnecessary Use of Checked Exceptions
72 Effective Java: Favor The Use of Standard Exceptions
73 Effective Java: Throw Exceptions Appropriate To The Abstraction
74 Effective Java: Document All Exceptions Thrown By Each Method
75 Effective Java: Include Failure-Capture Information in Detail Messages
76 Effective Java: Strive for Failure Atomicity
77 Effective Java: Don’t Ignore Exceptions
78 Effective Java: Synchronize Access to Shared Mutable Data
79 Effective Java: Avoid Excessive Synchronization
80 Effective Java: Prefer Executors, Tasks, and Streams to Threads
81 Effective Java: Prefer Concurrency Utilities Over wait and notify
82 Effective Java: Document Thread Safety
83 Effective Java: Use Lazy Initialization Judiciously
84 Effective Java: Don’t Depend on the Thread Scheduler
85 Effective Java: Prefer Alternatives To Java Serialization
86 Effective Java: Implement Serializable With Great Caution
87 Effective Java: Consider Using a Custom Serialized Form
88 Effective Java: Write readObject Methods Defensively
89 Effective Java: For Instance Control, Prefer Enum types to readResolve
90 Effective Java: Consider Serialization Proxies Instead of Serialized Instances
Today we are talking about the Cloneable
interface and it related clone
function. Honestly I haven’t interacted any with this method in the past and after learning about it I’m not sure I really want to, it seems quite error prone. So let’s dive in and understand this method so that we can make intelligent decisions about whether to use this method or not.
So what is the Cloneable
interface. It is a mixin interface that signals to users of the class that a certain action can be performed on the class. The extremely weird thing about this particular mixin is that, rather than requiring the implementation of a particular function, it merely acts as a flag that allows the implementing class to call a method on the parent class. What this means is that a user of the class that implements Cloneable
can’t necessarily always call the clone
method on that class without resorting to reflection and even then that might not work. All this being said, this is a part of the Object
class so it pays to understand it and know how to implement the method as well as what the alternatives are. This chapter of Effective Java goes through that.
So what is the contract of Cloneable
? As we learned above it doesn’t include any methods but instead acts as a flag to the protected clone
method in the Object
class. If a class calls the clone
on Object
and that class implements Cloneable
, Object
‘s implementation of clone
will return a field-by-field copy of the object. If the class does not implement Cloneable
, a CloneNotSupportedException
is thrown. If this use of a interface feels weird that is good, this is not a behavior you should try to mimic in your own classes. The general (although weak) contract is as follows:
- The implementing class should create a public
clone
class that calls into thesuper.clone()
method. -
(x.clone() != x) == true
simply, clone should return a new object and not just return the current object. -
(x.clone().getClass() == x.getClass() == true
this is not an absolute requirement but is expected. -
x.clone.equals(x) == true
Again this is not an absolute requirement but does decrease the surprise of how it would work.
One could think that you could just skip calling the super.clone()
method in your own clone
method and simply call into a constructor to create the new object but this could cause problems for a class that extends your class and calls into super.clone()
as it will return an object of the wrong class. As mentioned above, this doesn’t actually break the contract but goes against convention.
So let’s dive a little deeper into how this works and how you should implement it. The first step of implementing the clone
method is to call super.clone()
which will return a fully functioning replica of the calling class. If your class contains only primitives or references to immutable objects this may be all you need to do. Let’s see an example:
@Override
public Address clone() {
try {
return (Address) super.clone();
} catch (CloneNotSupportedException impossible) {
// This will never happen.
throw new AssertionException();
}
}
Enter fullscreen mode Exit fullscreen mode
Let’s go over some of the interesting things here. Because Object
‘s clone
method’s return type is Object
we want to cast it to the type of our class. This is fine because Java allows covariant types. Simply put, it allows us to use a subclass of the required class in the place of the parent type. This cast will always succeed and allows the client code to skip the type cast. The next interesting thing here is the try-catch
. The Object method’s signature includes that it throws a CloneNoteSupportedException
. In the case where a class implements the Cloneable
interface this exception will never be thrown, this is an example of a poor use of a checked exception and should have been a RuntimeException
.
So let’s look at an example where the class is a little more complex with non-primitive fields.
public class Stack {
private Object[] elements;
private int size;
private static final int DEFAULT_INITIAL_SIZE = 16;
public Stack() {
elements = new Object[DEFAULT_INITIAL_SIZE];
}
public void push(Object o) {
ensureCapacity();
elements[size++] = o
}
public Object pop() {
if (size == 0) {
throw new StackEmptyException();
}
Object result = elements[--size];
elements[size] = null;
return result;
}
private ensureCapacity() {
if (elements.length == size) {
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
}
Enter fullscreen mode Exit fullscreen mode
So if we want to make this Stack
class implement Cloneable
and tried to mimic what we did with the Address
class? We would end up with a replica class with a copied size
field but an Object
array that is shared between the two instances. This will lead to many issues so we need to take this a little farther. Think of the clone
method as a type of constructor that must protect the original object. So let’s see a working clone
method for this Stack
class:
@Override
public class Stack clone() {
try {
// this gets us a replica with copied size field
Stack copy = (Stack) super.clone();
copy.elements = elements.clone();
return copy;
} catch (CloneNotSupportedException impossible) {
throw new AssertionError();
}
}
Enter fullscreen mode Exit fullscreen mode
Now we are effectively cloning our Stack
class. This recursive call in our clone
method can solve a lot of problems with the clone method but not all. There are times that you will need to take this further and make deep copies of elements. There are many ways to accomplish this and we won’t go over all of them here but it is something to be aware of.
Other things to think about:
- Because
clone
methods are similar to constructors they shouldn’t call overridable methods. - Even though
Object
‘sclone
method throwsCloneNotSupportedException
, your overrides should not. - When designing a class for inheritance you have two choices. Implement the
clone
method with the same signature asObject
‘s, giving the implementing class the freedom to choose to implementCloneable
or not. The other option is to implementclone
and simply throwCloneNotSupportedException
which will block cloning. - If your class needs to be thread safe remember your
clone
implementation also needs to be synchronized.Object
‘sclone
method is not synchronized.
So is it worth it to implement this? Likely not. There are much easier ways to get this accomplished. Often a copy constructor or copy factory can get the job done in a much more straightforward way. So in our Address
case it could look like:
public class Address(Address originalAddress) { ... }
Enter fullscreen mode Exit fullscreen mode
or
public static Address newInstance(Address originalAddress) { ... }
Enter fullscreen mode Exit fullscreen mode
So what are some benefits of using one of these methods over implementing Cloneable
:
- They doesn’t rely on error prone, non-obvious behavior of the field-for-field copying.
- They don’t require following of non-obvious and undocumented contracts.
- Doesn’t conflict with the use of final fields
- Doesn’t have us deal with unnecessary checked exceptions.
- They allow parameters of types that are interfaces that the class implements. This is what we see done with collections in the standard library.
So long story short, you likely shouldn’t implement the Cloneable
interface. Instead reach for one of the other patterns we have such as copy constructors or copy factories. By using these methods you should have a much better experience and have a less buggy code base.
Effective Java Review (90 Part Series)
1 Effective Java Tuesday! Let’s Consider Static Factory Methods
2 Effective Java Tuesday! The Builder Pattern!
… 86 more parts…
3 Effective Java Tuesday! Singletons!
4 Effective Java Tuesday! Utility Classes!
5 Effective Java Tuesday! Prefer Dependency Injection!
6 Effective Java Tuesday! Avoid Creating Unnecessary Objects!
7 Effective Java Tuesday! Don’t Leak Object References!
8 Effective Java Tuesday! Avoid Finalizers and Cleaners!
9 Effective Java Tuesday! Prefer try-with-resources
10 Effective Java Tuesday! Obey the `equals` contract
11 Effective Java Tuesday! Obey the `hashCode` contract
12 Effective Java Tuesday! Override `toString`
13 Effective Java Tuesday! Override `clone` judiciously
14 Effective Java Tuesday! Consider Implementing `Comparable`
15 Effective Java Tuesday! Minimize the Accessibility of Classes and Member
16 Effective Java Tuesday! In Public Classes, Use Accessors, Not Public Fields
17 Effective Java Tuesday! Minimize Mutability
18 Effective Java Tuesday! Favor Composition Over Inheritance
19 Effective Java Tuesday! Design and Document Classes for Inheritance or Else Prohibit It.
20 Effective Java Tuesday! Prefer Interfaces to Abstract Classes
21 Effective Java! Design Interfaces for Posterity
22 Effective Java! Use Interfaces Only to Define Types
23 Effective Java! Prefer Class Hierarchies to Tagged Classes
24 Effective Java! Favor Static Members Classes over Non-Static
25 Effective Java! Limit Source Files to a Single Top-Level Class
26 Effective Java! Don’t Use Raw Types
27 Effective Java! Eliminate Unchecked Warnings
28 Effective Java! Prefer Lists to Array
29 Effective Java! Favor Generic Types
30 Effective Java! Favor Generic Methods
31 Effective Java! Use Bounded Wildcards to Increase API Flexibility
32 Effective Java! Combine Generics and Varargs Judiciously
33 Effective Java! Consider Typesafe Heterogenous Containers
34 Effective Java! Use Enums Instead of int Constants
35 Effective Java! Use Instance Fields Instead of Ordinals
36 Effective Java! Use EnumSet Instead of Bit Fields
37 Effective Java! Use EnumMap instead of Ordinal Indexing
38 Effective Java! Emulate Extensible Enums With Interfaces.
39 Effective Java! Prefer Annotations to Naming Patterns
40 Effective Java! Consistently Use the Override Annotation
41 Effective Java! Use Marker Interfaces to Define Types
42 Effective Java! Prefer Lambdas to Anonymous Classes
43 Effective Java! Prefer Method References to Lambdas
44 Effective Java! Favor the Use of Standard Functional Interfaces
45 Effective Java! Use Stream Judiciously
46 Effective Java! Prefer Side-Effect-Free Functions in Streams
47 Effective Java! Prefer Collection To Stream as a Return Type
48 Effective Java! Use Caution When Making Streams Parallel
49 Effective Java! Check Parameters for Validity
50 Effective Java! Make Defensive Copies When Necessary
51 Effective Java! Design Method Signatures Carefully
52 Effective Java! Use Overloading Judiciously
53 Effective Java! Use Varargs Judiciously
54 Effective Java! Return Empty Collections or Arrays, Not Nulls
55 Effective Java! Return Optionals Judiciously
56 Effective Java: Write Doc Comments For All Exposed APIs
57 Effective Java: Minimize The Scope of Local Variables
58 Effective Java: Prefer for-each loops to traditional for loops
59 Effective Java: Know and Use the Libraries
60 Effective Java: Avoid Float and Double If Exact Answers Are Required
61 Effective Java: Prefer Primitive Types to Boxed Types
62 Effective Java: Avoid Strings When Other Types Are More Appropriate
63 Effective Java: Beware the Performance of String Concatenation
64 Effective Java: Refer to Objects By Their Interfaces
65 Effective Java: Prefer Interfaces To Reflection
66 Effective Java: Use Native Methods Judiciously
67 Effective Java: Optimize Judiciously
68 Effective Java: Adhere to Generally Accepted Naming Conventions
69 Effective Java: Use Exceptions for Only Exceptional Circumstances
70 Effective Java: Use Checked Exceptions for Recoverable Conditions
71 Effective Java: Avoid Unnecessary Use of Checked Exceptions
72 Effective Java: Favor The Use of Standard Exceptions
73 Effective Java: Throw Exceptions Appropriate To The Abstraction
74 Effective Java: Document All Exceptions Thrown By Each Method
75 Effective Java: Include Failure-Capture Information in Detail Messages
76 Effective Java: Strive for Failure Atomicity
77 Effective Java: Don’t Ignore Exceptions
78 Effective Java: Synchronize Access to Shared Mutable Data
79 Effective Java: Avoid Excessive Synchronization
80 Effective Java: Prefer Executors, Tasks, and Streams to Threads
81 Effective Java: Prefer Concurrency Utilities Over wait and notify
82 Effective Java: Document Thread Safety
83 Effective Java: Use Lazy Initialization Judiciously
84 Effective Java: Don’t Depend on the Thread Scheduler
85 Effective Java: Prefer Alternatives To Java Serialization
86 Effective Java: Implement Serializable With Great Caution
87 Effective Java: Consider Using a Custom Serialized Form
88 Effective Java: Write readObject Methods Defensively
89 Effective Java: For Instance Control, Prefer Enum types to readResolve
90 Effective Java: Consider Serialization Proxies Instead of Serialized Instances
暂无评论内容