123 lines
3.5 KiB
Plaintext
123 lines
3.5 KiB
Plaintext
== Why is this an issue?
|
|
|
|
`Cloneable` is the marker `Interface` that indicates that `clone()` may be called on an object.
|
|
Overriding `clone()` without implementing `Cloneable` can be helpful if you want to control how subclasses clone themselves, but
|
|
otherwise, it's probably a mistake.
|
|
|
|
The usual convention for `Object.clone()` according to Oracle's Javadoc is:
|
|
|
|
. `x.clone() != x`
|
|
. `x.clone().getClass() == x.getClass()`
|
|
. `x.clone().equals(x)`
|
|
|
|
Obtaining the object that will be returned by calling `super.clone()` helps to satisfy those invariants:
|
|
|
|
. `super.clone()` returns a new object instance
|
|
. `super.clone()` returns an object of the same type as the one `clone()` was called on
|
|
. `Object.clone()` performs a shallow copy of the object's state.
|
|
|
|
== How to fix it
|
|
|
|
Ensure that the `clone()` method calls `super.clone()` and implement `Cloneable` in the class or remove the clone method.
|
|
|
|
=== Code examples
|
|
|
|
==== Noncompliant code example
|
|
|
|
[source,java,diff-id=1,diff-type=noncompliant]
|
|
----
|
|
class BaseClass { // Noncompliant - should implement Cloneable
|
|
@Override
|
|
public Object clone() throws CloneNotSupportedException { // Noncompliant - should return the super.clone() instance
|
|
return new BaseClass();
|
|
}
|
|
}
|
|
|
|
class DerivedClass extends BaseClass implements Cloneable {
|
|
/* Does not override clone() */
|
|
|
|
public void sayHello() {
|
|
System.out.println("Hello, world!");
|
|
}
|
|
}
|
|
|
|
class Application {
|
|
public static void main(String[] args) throws Exception {
|
|
DerivedClass instance = new DerivedClass();
|
|
((DerivedClass) instance.clone()).sayHello(); // Throws a ClassCastException because invariant #2 is violated
|
|
}
|
|
}
|
|
----
|
|
|
|
|
|
==== Compliant solution
|
|
|
|
[source,java,diff-id=1,diff-type=compliant]
|
|
----
|
|
class BaseClass implements Cloneable {
|
|
@Override
|
|
public Object clone() throws CloneNotSupportedException { // Compliant
|
|
return super.clone();
|
|
}
|
|
}
|
|
|
|
class DerivedClass extends BaseClass implements Cloneable {
|
|
/* Does not override clone() */
|
|
|
|
public void sayHello() {
|
|
System.out.println("Hello, world!");
|
|
}
|
|
}
|
|
|
|
class Application {
|
|
public static void main(String[] args) throws Exception {
|
|
DerivedClass instance = new DerivedClass();
|
|
((DerivedClass) instance.clone()).sayHello(); // Displays "Hello, world!" as expected. Invariant #2 is satisfied
|
|
}
|
|
}
|
|
----
|
|
|
|
|
|
== Resources
|
|
|
|
* CWE - https://cwe.mitre.org/data/definitions/580[CWE-580 - clone() Method Without super.clone()]
|
|
* https://wiki.sei.cmu.edu/confluence/x/FjZGBQ[CERT, MET53-J.] - Ensure that the clone() method calls super.clone()
|
|
|
|
|
|
ifdef::env-github,rspecator-view[]
|
|
|
|
'''
|
|
== Implementation Specification
|
|
(visible only on this page)
|
|
|
|
=== Message
|
|
|
|
* Use super.clone() to create and seed the cloned instance to be returned.
|
|
* Implement "Cloneable" in this class or remove the clone method.
|
|
|
|
|
|
'''
|
|
== Comments And Links
|
|
(visible only on this page)
|
|
|
|
=== is related to: S2975
|
|
|
|
=== on 7 Aug 2013, 09:59:21 Freddy Mallet wrote:
|
|
Is implemented by \http://jira.codehaus.org/browse/SONARJAVA-271
|
|
|
|
=== on 7 Aug 2013, 13:20:27 Dinesh Bolkensteyn wrote:
|
|
I fail to see why it would be mandatory to have the method throw the CloneNotSupportedException.
|
|
|
|
|
|
You can perfectly catch and propagate (as an unchecked exception) the instance thrown by super.clone().
|
|
|
|
So the PMD rule does not seem to make a lot of sense.
|
|
|
|
|
|
In any case, I doubt that this method is mean to verify this, isn't it?
|
|
|
|
=== on 8 Aug 2013, 06:27:34 Dinesh Bolkensteyn wrote:
|
|
Thanks for the updates Ann!
|
|
|
|
endif::env-github,rspecator-view[]
|