Implementing clone() for immutable classes
Asked Answered
P

5

7

I am developping a class library.

  1. I have an abstract base class Matrix for matrices that provides implementations for some of the basic methods.
  2. Derived from Matrix are concrete subclasses for different types of matrices.
  3. I have the requirement for matrices to be cloneable, so Matrix implements the Cloneable interface.
  4. Some of the classes derived from Matrix are immutable

Would it be acceptable for the immutable classes' clone methods that instead of returning a clone of the object, the object itself is returned?

Some (oversimplified) code for clarification:

abstract class Matrix implements Cloneable {
   ...
}

class ImmutableMatrix extends Matrix {
    ImmutableMatrix clone() {
        return this;
    }
    ...
}

class SomeOtherMatrix extends Matrix {
    SomeOtherMatrix clone() {
        SomeOtherMatrix other = super.clone();
        ...
        return other;
    }
    ...
}
Painty answered 3/11, 2012 at 9:36 Comment(2)
Why even allow your objects to be cloned in the first place?Viscera
Some operations (like transposition) can be easily implemented by returning a small wrapper around the original matrix and calculating values on the fly. Also, you can reduce memory footprint in some situations (i.e. operations on large sparse matrices). This works just fine as long as the original matrix is immutable. Otherwise any changes to the original matrix would have side effects on the result. But I have now solved it by providing a getImmutable() method that for immutable subclasses just returns the object itself, whereas mutable subclasses return an immutable copy. No more cloning.Painty
Q
11

I would have thought calling super.clone() would be sufficient.

If your class is immutable then it should have already cloned any mutable classes when it was constructed. Hence I would think it would be safe to have shallow copies of any fields your class has.

The JavaDocs state that x.clone() != x is preferred. While this isn't an absolute requirement, it would certainly be violated by your plan to just return this.

Quirk answered 3/11, 2012 at 9:51 Comment(0)
E
4

Just return this in the clone() implementation of immutable classes.

Encratia answered 9/11, 2012 at 16:4 Comment(0)
C
4

While you could have the immutable classes simply implement clone to return references to themselves, I really don't see much value in using clone on things that may or may not be mutable, absent some way of making mutable copes of immutable things and vice versa.

I would think it would be better for your base Matrix class to include methods IsImmutable and IsWritable, along with AsImmutable, AsMutable, and AsNewMutable methods; it should also include methods to read and write the matrix (though calling the "write" method on non-writable matrix should throw an exception).

Define static methods CreateImmutableMatrix and CreateMutableMatrix which, given a Matrix, will create a new immutable or mutable matrix which is pre-initialized with the proper data.

Mutable classes should implement AsImmutable to pass themselves to CreateImmutableMatrix, AsMutable to return themselves, and AsNewMutable to pass themselves to CreateMutableMatrix.

Immutable classes should implement AsImmutable to return themselves, AsMutable to call AsNewMutable, and AsNewMutable to pass themselves to CreateMutableMatrix.

Read-only wrappers should implement AsImmutable to call AsImmutable on the wrapped objects, and AsMutable and AsNewMutable to call AsNewMutable on the wrapped objects.

An object which receives a matrix which it may or may not need to copy or mutate may simply store it in a field (e.g. Foo). If it needs to mutate the matrix, it can replace Foo with Foo.AsMutable(). If the object containing the matrix needs to be copied, the field should be replaced in the copy with either Foo.AsImmutable() or Foo.AsNewMutable() depending upon whether the field in the copy will likely need to be mutated.

Clarkson answered 12/11, 2012 at 22:47 Comment(6)
Thank you for your reply. That pretty well describes the solution I have implemented in the meantime.Painty
@Clarkson I am implementing a similar scheme for Immutable/mutable classes with a parent view interface (I also saw this post: programmers.stackexchange.com/questions/221762/…). I see the purpose of the methods AsNewMutable and AsImmutable, but I am not sure about AsMutable. Wouldn't calling such a method on a mutable object indicate that you should have been passing that object as its mutable version anyway? Calling this method on an object implies that you might have side-effects or you might not.Bicuspid
@AlexanderKaratarakis: The idea would be that one would call AsMutable on references which were guaranteed to either (1) be immutable, or (2) hold the only reference in the universe to the mutable object in question--most likely one which was produced by calling "AsMutable" on an immutable object and which hadn't been shared since. Calling AsNewMutable on the universe's only reference to a mutable object would work, but it would be needlessly slow. If code is cloning an object with e.g. ten "expensive" properties, and it's likely that only one or two will need to be changed before...Clarkson
...the object is cloned again, using AsImmutable when making the properties and then using AsMutable whenever one needs to modify them would end up producing an extra copy of any property which was mutated before the object was cloned again, but would avoid the need to make new copies of things that weren't modified. This can make a huge difference in efficiency, especially when using nested collections. If e.g. a collection has ten nodes that hold ten items each, and two leaf nodes will be modified between cloning operations, using AsImmutable on the clone and AsMutable when...Clarkson
...making changes would mean that making the two modifications would require making mutable copies of two leaf nodes and one or two mid-level nodes, and the next clone operation would have to make immutable copies of those. Using AsNewImmutable for everything would mean that the changes wouldn't require any copying, but the next cloning operation would have to copy all 100 leaf nodes and all 10 mid-level nodes. Using AsMutable would thus turn 110 copy operations into four.Clarkson
@Clarkson Thank you very much for the detailed explanation. (btw, is AsNewImmutable in the last post a typo?)Bicuspid
B
1

Your class is not strictly immutable since it is not final: there can be mutable subclasses.

If someone wants to subclass ImmutableMatrix, he will not be able to implement clone() by calling super.clone() if you just return this. So in that case you should call super.clone().

However, if you make your class final, I don't see any reason why not just return this.

Bertelli answered 12/7, 2013 at 8:59 Comment(3)
If the contract for the class states that no aspect of state which has been observed will ever change, any subclass which violates that is broken. Even though it is almost never possible for any inheritable class to guarantee that subclasses will abide by its contract, common practice is to assume in non-security-related contexts that derived classes will do so. Were that not common practice, inheritance would be pretty much useless. I see no reason to regard immutability as fundamentally different from any other contractual promise in that regard.Clarkson
@Clarkson even if the subclass is immutable, it can implement clone by calling super.clone()Bertelli
If the subclass is immutable, even if not all derivatives of the base class are, it could implement clone by simply doing return this;. If class is specified by contract to be immutable, having implementing clone as return this may be acceptable even if the class is inheritable, provided that the contract documents that derived classes may not legitimately do anything that would clones to be distinct.Clarkson
L
-1

Yes If we see the behavior of String (Immutable class), If contents are same the same object is returned, so I thing your are right , clone() method should only return this.

Linguistician answered 3/11, 2012 at 10:5 Comment(2)
Yes String is not cloneable, but for saving memory if content of two objects are same and immutable that should return same object rather than creating other objectsLinguistician
That's what I was thinking too. At last I resorted to something different. The requirement to have Matrix implement Cloneable was because some methods had to create an immutable copy of the matrix passed as an argument. So I cloned the matrix argument inside those methods. Now, I added an abstract getImmutable() method to the matrix class and use that instead. This saves me from creating unnecessary clones for immutable classes while the mutable classes can provide their own implementation of this method by creating an immutable copy of the matrix.Painty

© 2022 - 2024 — McMap. All rights reserved.