Introduction
Object-oriented programming is a good thing, but sometimes it drives you crazy. For example, you have overridden some method in the class. How can you call the original method? Well, you can use super in Java or base in .NET to access the overridden method from your class. But what if you’d like to access the original method from another class?
In this case, you usually have to provide an additional method to retrieve the original value.
Call Overridden Function
Let’s investigate some important example: the functionObject.hashCode()
in Java or Object.GetHashCode()
in .NET play twofold role in both platforms: on the one hand, it allows to put effectively any object in some collection, on the other hand, the original value of the object’s hashcode is an identity of the object itself. When we put the object into a collection, we usually override hashCode()
/GetHashCode()
to represent the internal state of the object. To extract original hashcode Java and .NET provide special functions: System.identityHashCode
and RuntimeHelpers.GetHashCode
functions respectively.
We will take hashCode()
/GetHashCode()
method and examine it in more details.
Let’s MyClass
overrides Object
’s hashcode to be 1
:
In Java:
public class MyClass {
public int hashCode() {
return 1;
};
}
In .NET:
public class MyClass{
public override int GetHashCode() {
return 1;
}
}
If we have an instance of the MyObject
, how can we get the hashcode for original Object
class not using System.identityHashCode
in Java or RuntimeHelpers.GetHashCode
in .NET.?
For the first try, the problem looks trivial.
Object obj = new MyObject();
int hc = obj.hashCode();
Well, the hc-value
is 1
because public
methods in Java are virtual
(if not final
).
Let’s try to get Object
’s hash-code using reflection:
MyObject obj = new MyObject();
Method m = Object.class.getMethod("hashCode");
int hc = (int) m.invoke(obj);
Alas, the hc-
value is still 1
. It means, that although we took the Method
from Object.class
, it invokes the Method
of MyObject
class: virtual
methods in action!
Here, we used Java code, the .NET code is essentially the same and produces the same results.
For the second try, the problem looks unsolvable: the method was overridden and it seems impossible to obtain its original value (that’s why System.identityHashCode()
function exists). And this is true for JDK-5 and 6.
From JDK-7, we have powerful reflection methods which can solve this enigma.
Let’s look at this function:
private static MethodHandle getMethodHandle(Class<?> objClass) throws ReflectiveOperationException {
Class<?> parentClass = objClass.getSuperclass();
Lookup lookup = getLookup(objClass);
return lookup.findSpecial(parentClass, "hashCode", MethodType.methodType(int.class), objClass);
}
Here, we do the following:
- Create the appropriate object of
Lookup
class (see the implementation of getLookup(..)
function - Call
findSpecial
method of this class
The MethodHandle
object for hashCode
function is created. After that, we called its invoke()
method and viola, the correct result is received.
The .NET implementation is even more direct:
private delegate int HashCodeDelegate();
...
MethodInfo handle = objClass.BaseType.GetMethod("GetHashCode");
IntPtr ptr = handle.MethodHandle.GetFunctionPointer();
var hashCodeFun = (HashCodeDelegate)Activator.CreateInstance(typeof(HashCodeDelegate), obj, ptr);
var hashCode = hashCodeFun();
Results
Java:
Hashcode: 1, Object Hashcode:685325104, Identity Hashcode: 685325104
.NET:
Hashcode: 1, Object Hashcode: 46104728, Identity Hashcode: 46104728
In a different environment, one can see different values for Object Hashcode
and Identity Hashcode
, but always Object Hashcode
is the same as Identity Hashcode
.
The idea in .NET was provided in [2], I modified the solution a bit to match GetHashCode
function.
Acknowledgements
The author is grateful to Ilia Reznik and Alex Rafalovich for useful comments and recommendations.
Reference
- Oracle documentation for MethodHandle
- Stack overflow: how to call base.base.method()? (for .NET)