Search code examples
javadynamic-dispatch

What is this weird dynamic method dispatch behavior inside a static method in Java


The utility method in the below example is for illustration purposes only.

In the example below, instance method invocation was dispatched to reference type but not to the run-time object.

import java.sql.Timestamp;
import java.util.Date;
public class DynamicMethodDispatchEx {

    public static void main(String[] args) {
        Timestamp now = new Timestamp(System.currentTimeMillis());
        Timestamp beforeNow = new Timestamp(now.getTime() - 1);

        System.out.println("Finding newest in " + now + " and " + beforeNow);
        System.out.println("Attempt 1: " + staticFindNewer(beforeNow, now));
        System.out.println("Attempt 2: " + staticFindNewer(now, beforeNow));

    }

    public static Date staticFindNewer(Date one, Date two) {
        if (one.after(two)) {
            return one;
        } else {
            return two;
        }
    }

}

The below was the output I got

Finding newest in 2016-08-23 17:56:36.375 and 2016-08-23 17:56:36.374
Attempt 1: 2016-08-23 17:56:36.375
Attempt 2: 2016-08-23 17:56:36.374 // <---

After some investigation, I found out that java.util.Date.after(Date) was being invoked in the staticFindNewer() and the discrepancy in attempt 1 and 2 and was due to precision loss as Date's methods were being used.

However, I'm puzzled about dynamic dispatch. I expected Timestamp#after(Timestamp) to be invoked but Date#after(Date) was getting invoked. I thought the instance-method dispatch was always based on runtime object. Am I missing something silly (most probably)?


Solution

  • I expected Timestamp#after(Timestamp) to be invoked but Date#after(Date) was getting invoked. I thought the instance-method dispatch was always based on runtime object.

    Dynamic dispatch happens only on the invoked object, not on the arguments.

    So the call would go to Timestamp#after(Date) (because the compile-time type of the argument is Date, and the runtime type of the callee is Timestamp).

    Unfortunately, Timestamp does not override this method, so it defaults back to Date#after(Date) (which does not work too well here).

    So you have to make sure you call Timestamp#after(Timestamp) directly, or use the Date#compareTo(Date) method instead, which is properly implemented (and overridden in Timestamp).