rspec/rules/S1182/java/rule.adoc
2023-05-31 15:35:45 +02:00

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
* https://cwe.mitre.org/data/definitions/580[MITRE, 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[]