July 24, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Object Integrity & Security: Duplicating Objects: Part 2

  • June 11, 2007
  • By Matt Weisfeld
  • Send Email »
  • More Articles »

Cloning Inconsistencies

When you look at Diagram 1, you may notice one inconsistency that I pointed out in the previous article, the spot object still has the name defined as fido. In fact, the output in Figure 1 confirms this. This makes sense because you did clone the object, and this implies that all of the information is duplicated. Given this fact, how do you prove that the objects are indeed separate? Change the value of one, but not the other, as seen in Listing 3. Logically, you need to change the name of the spot object to "spot". You use the setName() method.

// Class Duplicate
public class Duplicate {

   public static void main(String[] args) {

      Dog fido = new Dog("fido", "retriever");

      Dog spot;

      spot = (Dog) fido.clone();

      spot.setName("spot");

      System.out.println("name = " + fido.getName());
      System.out.println("name = " + spot.getName());


   }
}

Listing 3: Change the name for the spot reference

When you execute this application, as seen in Figure 2, you now get the output you expect.



Click here for a larger image.

Figure 2. Cloning an Object

At this point, the graphical representation is in a logical state, the fido references and object with the name of fido and the spot references an object with the name of spot as seen in Diagram 2.

Diagram 2: An Object with two References and two Objects (different content)

Refining the Cloning Mechanism

In the previous example, you did the actual copy by brute force, meaning that, after you cloned the object, you called the setName() method directly to set the new name.

spot = (Dog) fido.clone();

spot.setName("spot");

Although this works, you can search for a more elegant way to accomplish this task. The obvious way to set the appropriate attributes is to send them over the clone() method's parameter list.

spot = (Dog) fido.clone(String name, String breed);

Give this a try and see what actually happens. You will do this in a few steps because understanding the process by which the compiler operates here is quite educational. First, you will actually update the clone() method itself to include the parameters.

public Object clone(String n, String b) {
   try {
      return super.clone();
   } catch (CloneNotSupportedException e) {
      throw new InternalError(e.toString());
   }
}

When you compile this code, you get the error message seen in Figure 3.

Figure 3: Error Message

Actually, this is entirely expected because the corresponding signature in the application does not match. You can take care of this by updating the application code.

spot = (Dog) fido.clone("spot", "mutt");

Although this compiles (as seen in Figure 4), note that the Object class does not have a clone() method with this signature. And, the name for the spot object somehow has reverted back to 'fido'.

Figure 4: Application with Updated clone() Signature

After a brief inspection, it becomes obvious that there is nowhere in the clone() method that actually sets the attribute for the new object.

public Object clone(String n, String b) {
   try {
      // We just return the object reference . nothing else happens

      return super.clone();

   } catch (CloneNotSupportedException e) {
      throw new InternalError(e.toString());
   }
}

So, put in some code to set the attributes using the parameters provided.

public Object clone(String n, String b) {
   try {
      name = n;
      breed = b;

      return super.clone();

   } catch (CloneNotSupportedException e) {
      throw new InternalError(e.toString());
   }
}

However, when you run the application, as in Figure 5, you now have both objects with name attributes 'spot'.

Figure 5: Add Parameter Assignments to clone() Method

This might be confusing at first; however, take a look at the placement of the assignments. The assignments are, in fact, being made prior to the cloning itself. Thus, the change is being made to the original 'fido' object and then subsequently to the new 'spot' object. This is why both have the same name assignments.

Because the call to clone() is done within the actual return statement, you can't update the new object at this point. One approach is to create a second reference and use that prior to returning the new object.

   Dog ref;

...

   public Object clone(String n, String b) {
      try {
         ref = (Dog) super.clone();

         ref.setName(n);
         ref.setBreed(b);

         return ref;

      } catch (CloneNotSupportedException e) {
         throw new InternalError(e.toString());
      }
   }

When this is attempted, you get a compilation problem as seen in Figure 6. In fact, several of the potential solutions that you might come up with present various design problems. This realization leads you to the following conclusion: Even though there are many design variations that you could attempt in an effort to refine the clone() method to suit your purposes, they are all staring to get a bit complicated.

One of the programming mantras that I like to use is "keep things as simple as possible". Thus, it is intuitively simpler to keep the clone() method as it is and then create another method to wrap the clone() method; this will provide the functionality that you want. Not only is this a simpler approach, but it also maintains the concept of reusability (and responsibly) because the clone() is only responsible for cloning the object as defined in the Object class documentation. Any refinement or added functionality is added in the more-specific wrapper class.

Figure 6: Problems with updating the clone() Method

The next step is to create the wrapper class itself.





Page 2 of 3



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel