views:

83

answers:

3

Hi I got the code from a book:

public class Container {

    Map<String, Object> components;

    public Container() {
     components = new HashMap<String, Object>();

     Properties properties = new Properties();
     try {
      properties.load(new FileInputStream("components.properties"));
      for (Map.Entry entry : properties.entrySet()) {
       String key = (String) entry.getKey();
       String value = (String) entry.getValue();
       processEntry(key, value);
      }
     } catch (Exception ex) {
      throw new RuntimeException();
     }

    }

    private void processEntry(String key, String value) throws Exception {
     String parts[] = key.split("\\.");

     if (parts.length == 1) {
      Object component = Class.forName(value).newInstance();
      components.put(parts[0], component);
     } else {
      Object component = components.get(parts[0]);
      Object reference = components.get(value);
      PropertyUtils.setProperty(component, parts[1], reference);
     }

    }

    public Object getComponent(String id) {
     return components.get(id);
    }

}

My question is, on the line

PropertyUtils.setProperty(component, parts[1], reference);

The object's property in the Map is changed. Even though after the property is updated there's no component.put() to update the object inside the map, the object is updated. Why is that?

+8  A: 

It's because the map only contains a reference to the object - not a copy of the object.

When you change something in the object, you can see that change however you get to it - whether it's via the reference in the map or not.

This is exactly the same as doing (say):

StringBuilder first = new StringBuilder();
StringBuilder second = first;

first.append("Hello");
System.out.println(second); // Prints "Hello"

Here both first and second are references to the same StringBuilder object... when you change the contents of that object via first.append(), that change is still visible when you look at the object via the second reference in the subsequent line.

Jon Skeet
you've not declared second
Rich Seller
@Rich: Thanks, fixed.
Jon Skeet
+1  A: 

In java every object is a reference to an object. So if you put an object in a map, only the reference is stored and not a copy of the object. Hence each update to objects in a map will be seen when looking it up from the map. Because you will get a reference to the same object when retrieving it from the map.

Thirler
It's inaccurate to say that every object is a reference to an object, as this is an infinitely recursive statement. You should say that every *variable* is a reference to an object, excluding primitive-type variables.
Hosam Aly
A: 

All handles ( where handle is a fields, variables etc ) in Java are references not copies of the Object itself.

The problem you are experiencing is because your objects are mutable - ie they have setters or similar methods that change internal state ( something write to said objects fields ). The only way to ever prevent anything anywhere in your system from make changes you wish to prevent is to model stuff that shouldnt change as immutable classes.

Immutability

Its often considered good practice to create classes that are immutable. Immutable classes only ever write to their fields in the constructor. All other methods on the Immutable class are read only.

The JDK contains several good examples of immutable classes, including the following:

  • String, all methods create a new String, often sharing the char[] from the original String.
  • Integer and all the other primitive wrappers.

Builder

The builder pattern may be used to progressively create a mutable object using several setters. Once completed the build method may be invoked to create the immutable object. StringBuilder/Buffer are good examples of the builder pattern with String the product.

mP