views:

253

answers:

2

Hi,

In the code below I create a Pen object and initialize it's color to white. In the constructor of Pen, after setting the field 'penColor' to the value passed into the constructor, I update a global static weak hashmap that I'm keeping where the KEY is the 'this pointer - in my case a Pen, and the value is another weakhashmap whose KEY is the string "penColor" and whose value is a reference to the penColor member field.

Next, my code updates the Pen's color with a call to the Pen's setColor function. I would have thought that after this update, if I looked up the Pen object's color field in my weakhashmap, it would reflect the new color, but it does not. Can someone explain why that is?

package weakhashmaptest;


import java.awt.Color;
import java.util.WeakHashMap;
import java.util.Iterator;

public class Main {

    static WeakHashMap <Object, WeakHashMap>ownerMap = new WeakHashMap<Object, WeakHashMap>();

    public static void main(String[] args) {

        Pen r = new Pen(Color.WHITE);

        Iterator i = ownerMap.keySet().iterator();

        while(i.hasNext()) {
            Object key = i.next();
            System.out.println("\telement of hashmap is : " +ownerMap.get(key));
        }

        r.setColor(Color.BLACK);

        System.gc();

        i = ownerMap.keySet().iterator();

        while(i.hasNext()) {
            Object key = i.next();
            System.out.println("\telement of hashmap is : " +ownerMap.get(key));
        }

    }

    public static void mapUpdate(Object owner, Object reference, String field_name) {

        WeakHashMap reference_map = ownerMap.get(owner);

        if (reference_map == null) {         
            reference_map = new WeakHashMap();
            reference_map.put(field_name, reference);
        } else {         
            reference_map.put(field_name, reference);
        }

        ownerMap.put(owner, reference_map);

    }

}

class Pen {

    Color penColor;

    public Pen(Color c) {

        penColor = c;
        Main.mapUpdate(this, penColor, "penColor");

    }

    public void setColor(Color c) {

        penColor = c;

    }

}
+5  A: 

You're putting a reference to the Color object into the map, rather than the Pen. If you put the Pen into the map, and then asked for the pen's colour later on, you'd see the change.

To put it in very simplistic terms, what you're doing is similar to:

Pen pen = new Pen(Color.WHITE);
Color color = pen.penColor;
pen.setColor(Color.BLACK);
// color here still refers to Color.WHITE, not Color.BLACK.
Jon Skeet
Hi John, Thanks for the prompt response. I think I understand what you're saying, but it still is hard to digest. How is the Pen color different than the Pen. Aren't they both references? Notice I am keeping the Pen in the map, as a KEY.
Amir Afghani
You're keeping the Pen as the key, but you're using the *initial colour* as the value. It's just a reference to an object - it's not a reference to the expression "this.penColor" or anything like that.
Jon Skeet
Should I take your acceptance as understanding? If not, please do add more comments and we can go through this as much as you like. I think it's worth taking out the nested maps from the equation though - they're not really relevant, but they may be making it harder for you to think about.
Jon Skeet
I know you're right because when I update my example with your suggestion it works. I'm trying to achieve the following:I'm doing bytecode instrumentation inside of the constructor of an object. After each field is initialized, I update a weak hashmap that says : The owner object is 'this', and it's value is another hashmap whose key is the name of the field, and whose value is a reference to the field.Now, I would have hoped that what this buys me is the following : When the references are updated, I don't have to do more BCI, I can rely on the maps pointer to the reference.
Amir Afghani
I was limited to 600 characters in my last message, so I had to be super concise. I'm not asking you to solve this problem for me, simply to explain if my approach is feasable, and if not, why. I guess I'm confused on references and pointers here...
Amir Afghani
OK, if I accept this statement by you : 'it's not a reference to the expression "this.penColor" or anything like that', then I completely understand. The question then is, is it possible to get a reference to "this.penColor".
Amir Afghani
Basically you'd have to store a reference to the Pen (which you already have) and find out the current field value with reflection. It'll be ugly, I'm afraid.
Jon Skeet
That was my suspicion as well. Thanks for taking the time to clarify this for me.
Amir Afghani
A: 

Here's the code that reflects my expected observation based on Jon's explanation:

import java.lang.reflect.Field;
import java.util.WeakHashMap;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Main {

    static WeakHashMap <Object, WeakHashMap>ownerMap = new WeakHashMap<Object, WeakHashMap>();

    public static void main(String[] args) {

        Pen r = new Pen(Color.WHITE);

        Iterator i = ownerMap.keySet().iterator();

        while(i.hasNext()) {
            Object key = i.next();
            WeakHashMap value = ownerMap.get(key);
            Iterator <String> whm_iter = value.keySet().iterator();

            while(whm_iter.hasNext()) {

                String whm_key = whm_iter.next();
                Field my_field;

                try {
                    //do some reflection to get the value ....
                    my_field = key.getClass().getDeclaredField(whm_key);
                    Object field_val = my_field.get(key);
                    System.out.println("owner = " +key+ "\treference = " +field_val);
                } catch (NoSuchFieldException ex) {
                } catch (SecurityException ex) {
                } catch(IllegalAccessException ex) {
                }

            }

        }

        r.setColor(Color.BLACK);

        System.gc();

        i = ownerMap.keySet().iterator();

        while (i.hasNext()) {
            Object key = i.next();
            WeakHashMap value = ownerMap.get(key);
            Iterator<String> whm_iter = value.keySet().iterator();

            while (whm_iter.hasNext()) {

                String whm_key = whm_iter.next();
                Field my_field;

                try {
                    //do some reflection to get the value ....
                    my_field = key.getClass().getDeclaredField(whm_key);
                    Object field_val = my_field.get(key);
                    System.out.println("owner = " + key + "\treference = " + field_val);
                } catch (NoSuchFieldException ex) {
                } catch (SecurityException ex) {
                } catch (IllegalAccessException ex) {
                }

            }

        }

    }

    public static void mapUpdate(Object owner, String field_name) {

        WeakHashMap reference_map = ownerMap.get(owner);

        if (reference_map == null) {
            reference_map = new WeakHashMap();
            reference_map.put(field_name, null);
        } else {
            reference_map.put(field_name, null);
        }

        ownerMap.put(owner, reference_map);

    }

}

class Pen {

    Color penColor;

    public Pen(Color c) {

        penColor = c;
        Main.mapUpdate(this, "penColor");

    }

    public void setColor(Color c) {

        penColor = c;

    }

}
Amir Afghani