Defensive object copies in java and how scala avoids it

In one word: immutability. But read on for the whole history =)

In another, old post, I wrote about the usage a primitive long value instead of Date objects in attributes. This kind of thing is usually called defensive copy, i.e, we copy the value we received in a setter or constructor, instead of assigning it directly. We could have, also, copied the date as a new date object. This may sound simple, but wait until you have big objects, with complex hierarchies.

In theory, the clone method could help us solve this problem. I wrote about this method a long while ago: the bads of the java clone method, illustrating a bit how the java language sometimes makes us think the wrong way. The Effective Java book explain this problem also, so maybe you’ll want to check it out.

But that analysis on the clone method was by no means complete. More than what I mentioned previously, there is at least one more thing that is bad. Extracted from the javadoc:

By convention, the object returned by this method should be independent of this object (which is being cloned). To achieve this independence, it may be necessary to modify one or more fields of the object returned by super.clone before returning it.

It means that, whatever it does, the clone method doesn’t return a real copy of the object, but a shallow one instead. And it goes as far as saying that you are responsible to make the copy right, by modifying fields… now imagine doing that with an object that references a couple of others, which by turn references others and others… exponentially difficult to get it right.

class A(b: B) extends Cloneable {
  override def clone() = super.clone
  override def toString() = "[A: %s]".format(b)
}

class B(c: C) extends Cloneable {
  override def clone() = super.clone
  override def toString() = "[B: %s]".format(c)
}

class C(var x: Int) extends Cloneable {
  override def clone() = super.clone
  override def toString() = "[C: %d]".format(x)
}

val c = new C(10)
val b = new B(c)
val a = new A(b)

val a2 = a.clone
c.x = -99

The sample code above is in Scala, but Java code would be very similar. So, is the code correct? When I do c.x = -99, should it affect only a, or both a and a2? Currently it does the later:

scala> println(a)
[A: [B: [C: -99]]]

scala> println(a2)
[A: [B: [C: -99]]]

So here is where the recursive cloning would take place. I would have to make the clone method inside A call the clone method from B, and so on. Not fun. The java language is not nice here, but perhaps the problem is deeper. Lets try to change our way of thinking.

After a few years in Scala land, I find there is something more to add. One thing that Scala tries to convince you is that you should have immutable objects as much as possible. That means that your objects never changes – you create new ones if need be. That also means that they can be shared easy and safely – thus clone is not necessary anymore.

Back to the sample code above, c.x = -99 would never be allowed – so it isn’t a problem at all. To make it happen, our new C class would look like this:

class C(x: Int)

Removing the var keyword effectively makes x immutable. What if you really want a new x? You have to create a whole new A. If this class had more fields, it could be boring, so scala helps us here. Lets change our classes to be immutable, forget about cloning stuff, and to be case classes. Lets also add another attribute to A.

case class A(b: B, name: String)
case class B(c: C)
case class C(x: Int)

val c = C(10)
val b = B(c)
val a = A(b, "jcranky")

First, being case classes, we don’t need to bother with the toString overrides anymore. Nor with the new operator. And we also got a prize: the copy function. With this, we can copy an object and change only what we want. This is a sample execution of out new classes:

scala> println(a)
A(B(C(10)),jcranky)

val a2 = a.copy(b = B(C(-99))

scala> println(a)
A(B(C(10)),jcranky)

scala> println(a2)
A(B(C(-99)),jcranky)

The only drawback is that you have to design your application with this kind of structure in mind. From that on, only bonuses. You don’t have too much to worry with locking for example, since there is no mutable stable to watch over.

Advertisements
This entry was posted in java, scala and tagged , , , , , , , , , . Bookmark the permalink.

One Response to Defensive object copies in java and how scala avoids it

  1. Pingback: Akka Essentials Book Review « JCranky's Blog!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s