Mutable, Immutable, My Black Hen
Jan. 30th, 2014 07:21 pmAt some point late this morning, I came to the horrible realization that the absence of the "const" keyword in Java, coupled with the absence of operator overloading, meant that all of the code that I was porting across from C++ had an ugly, ugly hole in it.
I have ID classes that I use to locate several types of objects in the C++ code. In C++, I would write code like:
The first case wouldn't make a copy of the const reference that GetID() returns and I wouldn't be allowed to change it, while the second line would assign the contents of the const reference to my new PdID, since I'd overloaded operator=. And all was right with the world.
Except when I converted that to Java, I ended up with:
Now in this case, I have taken the reference that pPd.GetID() has returned that I did not want to modify and am happily going to modify it, because I didn't -- and couldn't -- define the function return as const. Ick.
So I have now made PdID immutable by removing all of the mutators that belong to the class. And I've added a MutablePdID which extends PdID and contains all of the mutators that are no longer in PdID. Add toMutable() and toImmutable() methods to each class that will create new objects as required, and my IDs Formerly Known As Const are now safe from the ravages of clueless coding.
The result looks like:
This was an exciting way to spend a day of work...
I have ID classes that I use to locate several types of objects in the C++ code. In C++, I would write code like:
const CPdID& id = pPd->GetID();
PdID modifiableID = pPd->GetID();
The first case wouldn't make a copy of the const reference that GetID() returns and I wouldn't be allowed to change it, while the second line would assign the contents of the const reference to my new PdID, since I'd overloaded operator=. And all was right with the world.
Except when I converted that to Java, I ended up with:
PdID id = pPd.GetID();
PdID modifiableID = pPd.GetID();
Now in this case, I have taken the reference that pPd.GetID() has returned that I did not want to modify and am happily going to modify it, because I didn't -- and couldn't -- define the function return as const. Ick.
So I have now made PdID immutable by removing all of the mutators that belong to the class. And I've added a MutablePdID which extends PdID and contains all of the mutators that are no longer in PdID. Add toMutable() and toImmutable() methods to each class that will create new objects as required, and my IDs Formerly Known As Const are now safe from the ravages of clueless coding.
The result looks like:
PdID id = pPd.GetID();
MutablePdID modifiableID = new MutablePdID( pPd.GetID());
// The case below is not preferred (by me), because if GetID() returned a MutablePdID,
// I could change the returned object without intending to. Oops.
//MutablePdID modifiableID = pPd.GetID().toMutable();
This was an exciting way to spend a day of work...
no subject
Date: 2014-01-31 02:51 am (UTC)Take a look at Lombok.org, which makes this sort of thing totally painless.
no subject
Date: 2014-01-31 04:18 am (UTC)no subject
Date: 2014-01-31 04:19 am (UTC)no subject
Date: 2014-02-02 05:38 am (UTC)Their package names are all org.lombok; that's what threw me off.
There's a @Value annotation that creates an immutable object with an all-args constructor and equals, hashCode, and toString methods.
no subject
Date: 2014-02-02 05:47 am (UTC)(You can also store them in a hash table and make a factory method that guarantees that instances are unique, in the sense that a.equals(b) iff a == b. This can be used to speed up tests.)
Note that Java doesn't have C++'s weird syntactic distinction between references and pointers. All objects are represented by pointers, but one can have "final" variables that can't be modified.
no subject
Date: 2014-02-02 06:10 am (UTC)The problem is that I need mutable versions of some of the IDs, because I'm doing math (or other similar translations) on them. Take, for example, my time period ID. I may be starting out with a time period ID that is pointing to a deal period (a zero-length time period during which a transaction occurs). I may want to execute a five month lag to arrive at a month end period, except that the deal period is a weekly period. So I have to (in rapid succession):
In theory, I could write a general purpose function that would magically do all of those things in one operation and return the new time period ID. In practice, that would be a PITB, especially since there are many, many variations on that sort of instruction that a user might give my code to execute.
So I have a mutable time period ID to go along with the immutable one so I can perform multiple operations on it before converting it back to an immutable ID (if I need one) for storage. Or I can just look up the matching time period in my hash table, which is probably why I was doing all that math in the first place. :)
no subject
Date: 2014-02-02 06:16 am (UTC)no subject
Date: 2014-02-02 07:29 pm (UTC)no subject
Date: 2014-02-02 07:30 pm (UTC)no subject
Date: 2014-02-04 04:19 am (UTC)If I only had immutable PdIDs, then I'd potentially be creating lots of extra objects when doing the math. Right now, I just put:
storedID = newID.toImmutable();
in my setter in the class. If newID is already immutable, then I don't create a new object. If not, then I have to create a new immutable object to store, but I'm sure the object that I'm storing is immutable.