Wednesday, December 22, 2010

null argument method ambiguity resolution

This is a summary of what I understood from a quite interesting discussion[1]. Think of a case as follows

public class MyClass {
MyClass(CharSequence charSeq) {
System.out.println("CharSequence");
}
MyClass(String s) {
System.out.println("String");
}
public static void main(String[] args) {
new MyClass(null); // prints "String"
new MyClass(""); // prints "String"
new MyClass((CharSequence) null); // prints "CharSequence"
new MyClass((CharSequence) ""); // prints "CharSequence"
}
}
Now to understand why its working in this way, we need to get into policy defined in java (http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12.2.5) for compile time method resolution.

Java uses two pass approach, in first pass find all applicable methods, if more than one such method, in second pass use most specific method if can be determined else ambiguity issue.

15.12.2.5 Choosing the Most Specific Method

If more than one member method is both accessible and applicable to a method invocation, it is necessary to choose one to provide the descriptor for the run-time method dispatch. The Java programming language uses the rule that the most specific method is chosen.

The informal intuition is that one method is more specific than another if any invocation handled by the first method could be passed on to the other one without a compile-time type error.

This means that if you have two methods MyClass(one with CharSequence and another with String type argument) which are valid for a call MyClass(null), it will choose the String one because String Implements ( for general Classes case either Implements or Extends) CharSequence so linking it to String will give no error even if you pass String to a MyClass(CharSequence) method. But you can't do vice versa. Hence a subclass argument method is chosen

There are few more cases. If I add one more method as follows

    MyClass(ArrayList list) {
System.out.println("Array List");
}

Now it will be a compile time error because all three (ArrayList, String and CharSequence) doesn't form a type hierarchy (one extends/implements another) causing an ambiguity for compiler. However if I add following rather than above ArrayList method

    MyClass(Object obj) {
System.out.println("Object");
}

it will work because String implements CharSequence extends Object, So an String can be casted to both hence compiler can choose String argument method for linking.

One more case (might not be that interesting)

Adding following won't create a problem because primitive type is not applicable for null so this method will be removed in first pass of complier for applicable methods.

    MyClass(int i) {
System.out.println("int");
}

however following is an object and hence will create ambiguity problem

    MyClass(Integer i) {
System.out.println("Integer wrapper");
}


Example above is taken from following link


[1] http://stackoverflow.com/questions/2659496/how-does-polymorph-ambiguity-distinction-work