Some confusing cases of method overloading – Part 2

Some days ago I posted about an example program which called an overloaded method where its parameters are final variables. This time I’ll call an overloaded constructor where its actual parameter is a null literal. As per the Java Language specification, overloaded constructor resolution is identical to overloaded method resolution, so for methods these same concepts apply.

Consider this example, extracted from the (very good and entertaining) book Java Puzzlers, which is a perfectly legal Java program:

public class Confusing {

  private Confusing(Object o) {
    System.out.println("Object");
  }

  private Confusing(double[] dArray) {
    System.out.println("double array");
  }

  public static void main(String[] args) {
    new Confusing(null); // prints double array
  }

}

Here, the class Confusing provides an overloaded constructor: one of them take an Object as a parameter and the other an array. Now, as the Java Language Specification says in its Chapter 10: arrays are objects, are dynamically created, and can be assigned to variables of type Object. All methods of the class Object can be invoked on an array.

So we have that double[] is a subclass of Object, and we try to create an instance of the class Confusing with its actual parameter being a null literal. Because null can be assigned to a double[] array, and to an Object instance, then both methods are applicable, however as per the Java Language Specification, the overloaded method to be called is the most specific one, and because an array is a subclass of Object, then the most specific is the Confusing(double[]) constructor, so the compiler chooses this constructor and then the program compiles without error on Java 6 update 20. Tools like FindBugs and PMD didn’t find this weird, as no warning was emitted from them.

Is it ambiguous or not?

Clearly the above program is perfectly legal…for the compiler. For me, and I believe many others average Joe programmers it is confusing. Why it is legal when at first glance it seems it is ambiguous? My rule of thumb is that code must be easy to read, because code is read more times than written. Also, for productivity purposes the compiler doesn’t count as somebody that read code, so why write perfectly well-formed code that is difficult to grasp at first sight?

The code is ambiguous because a null literal could be used as actual parameter for both constructors, it is not clear which one it will choose to call. Whether or not the Java compiler actually has very deterministic and definite rules to disambiguate the call, it is irrelevant to the human (and error prone) reader, and therefore it is sound to emit a Warning message here.

How to disambiguate

Casting the null parameter to the actual type you intent to refer to is the simplest way to disambiguate the call. No question which constructor you are referring to now:

new Confusing((Object)null);
new Confusing((String[])null);

or you can define a variable with the right type:

Object x = null;
new Confusing((x);
double[] y = null;
new Confusion(y);

Conclusions

The rules of overloaded method resolution are complicated to understand, and “smart” code that depends on those rules makes the program harder to read. I would personally like to see FindBugs or PMD add some rule to detect this kind of situation. As said in the first part of this series of posts, why don’t we use the Java type system to the fullest?

Stay tuned for more interesting overloaded examples!

One thought on “Some confusing cases of method overloading – Part 2

Leave a Reply

Your email address will not be published. Required fields are marked *