Does it make sense to implement a copy method on an immutable type, returning a new instance? Or should it just be the current instance?
I thought the type doesn't change anyway so why copy? Like no one copies the number 5, right?
Does it make sense to implement a copy method on an immutable type, returning a new instance? Or should it just be the current instance?
I thought the type doesn't change anyway so why copy? Like no one copies the number 5, right?
One of the advantages of immutable types is that they can be interned (e.g., Java strings). Certainly you shouldn't make extra copies if you can avoid it.
Technically, an integer is a value type so it is copied constantly. :)
That said, making a copy of an immutable object doesn't make sense. The examples of strings provided by others seems to be a band aid on abstraction leakage by those classes.
Well Java's String class has this:
String(String original)
Initializes a newly created String object so that it represents the same
sequence of characters as the argument; in other words, the newly created
string is a copy of the argument string.
and .Net's has the Copy()
method which does the same.
Both frameworks were designed by people who are smarter than I am, so there must be a good reason - someone at sometime needs strings that are different reference-wise, but have the same value.
I'm just not sure when that would be...
There are certain cases where it makes sense. Java strings are a good example. When a string is created in Java, it has a reference to a backing array of characters (a char[]
). It knows the offset into the char array, and the length of the string. When you create a substring, that refers to the same backing array. Now consider this code:
String x = buildVeryLongString();
String y = x.substring(0, 5);
// Use y a lot, but x is garbage collected
The fact that y
is still in the system means that the original char[]
used by x
is still required. In other words, you're using more memory than you have to. If you change the code to:
String x = buildVeryLongString();
String y = new String(x.substring(0, 5));
then you'll end up copying the data to a new char[]
. When x
and y
have rougly the same lifetimes this approach wastes memory (by having two copies) but in the case where x
is garbage collected before y
, it can make a big difference.
I've run into a similar example with strings in real life, when reading words from a dictionary. By default, BufferedReader.readLine()
will use a buffer of 80 characters for a line to start with - so any (non-empty) string returned by readLine()
will refer to a char[]
array of at least 80 characters. If you're reading a dictionary file with one word per line, that's a lot of wasted space!
This is just an example, but it shows the difference between two immutable objects which are semantically equivalent in terms of what you do with them, but have different characteristics in other ways. That is usually at the heart of why you'd want to copy an immutable type - but it's still a pretty rare thing to want to do.
In .NET, strings are stored somewhat differently - the character data is held within the string object itself instead of in a separate array. (Arrays, strings and IntPtr
are the only variable-size types in .NET, as far as I'm aware.) However, the "buffer" in the string can still be larger than it needs to be. For example:
StringBuilder builder = new StringBuilder(10000);
builder.Append("not a lot");
string x = builder.ToString();
The string object referred to by x
will have a huge buffer. Changing the last line to builder.ToString().Copy()
would make the large buffer eligible for garbage collection immediately, leaving a small string instead. Again, doing this unconditionally is a bad idea, but it can be helpful in some cases.
Does it make sense to provide a 'Copy' operation on an immutable object?
No.
(There is lots of other interesting discussion in the other answers, but I thought I'd provide the short answer.)
If said object needs to implement an interface that requires a Clone() method (or moral equivalent), it is fine to 'return this'.
I'll assume we mean objects (classes), since it is a moot point for structs.
There are a few dubious reasons for cloning an immutable object:
readonly
fields can be changed by reflection) - perhaps for some super-security conscious codeIf we extend the discussion to consider deep cloning, then it becomes more reasonable, since a regular immutable object doesn't imply that any associated objects are also immutable. A deep clone would fix this, but is a separate consideration.
I think maybe the remoting scenario is the best I can do...