Archive for January, 2007

Equality problem with an Object and its dynamic proxy.

January 11, 2007

I met with this problem while working on something else. In a hashmap I was using an object and its proxy as the key. What I saw was that, even if I put both the object and proxy, the hashmap will take only one object. I was confused for some time and then I realized that internally, when HashMap invokes ‘equals’ method, the InvocationHandler of the proxy delegates that method as well to the actual object, so that will always return true. Hence, I will never be able to have both as keys. To make it clear

MyObject obj ; //say it is some valid object implementing MyIntf interface.ProxyOfMyObject proxy ; //say this is the valid proxy of obj

obj.equals(proxy) returns false. That is understood.

proxy.equals(obj) will return true. This happens because the invocation handler will call the obj.equals(obj) internally.

To fix this, I created one more interface ‘ForEqualsMyIntf’ that extends ‘MyIntf’. This interface doesn’t have any methods. I will create the proxy using this ‘ForEqualsMyIntf’ interface, rather then the usual ‘MyIntf’. And in the ‘inovke’ method of Invocation Handler, I will check if the object passed in ‘equals’ method is instanseof ‘ForEqualsMyIntf’. So, when proxy.equals(obj) is invoked, since the obj is not implemeting ‘ForEqualsMyIntf’, it will return false. I am giving the sample code for this invocation handler below.

class MyInvocationHandler implements InvocationHandler {
  private MyObject obj;
public MyInvocationHandler(MyObject obj) {
  this.obj = obj;
  }

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  if(method.getName().equals("equals")) {
  Object other = args[0];
  if(other instanceof ForEqualsMyIntf) {
  if(other == proxy) {
  return new Boolean(true);
  } else {
  return new Boolean(false);
  }
  } else {
  return new Boolean(false);
  }
  }
  Object result = method.invoke(obj, args);
  if(method.getName().equals("toString")) {
  return "Proxy - " + result;
  }
  return result;
  }
  }