Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overloaded method selection based on the parameter's real type

Tags:

java

oop

People also ask

Can methods be overloaded based on the return types?

No, you cannot overload a method based on different return type but same argument type and number in java. same name.

Which method is overloaded method?

Overloading happens when you have two methods with the same name but different signatures (or arguments). In a class we can implement two or more methods with the same name. Overloaded methods are differentiated based on the number and type of parameter passed as arguments to the methods.

What is true about overloaded methods?

Which of the following is true about overloaded methods? The correct answer is A. Overloaded must have the same name but different parameter lists.

Where is method overloading used in real time?

One scenario where method overloading could be useful is if you are building some public API. For example ASP.NET MVC use it to provide functionality through HTML helpers where you have the same method overloaded multiple times with different arguments.


I expect the method selection to take in consideration the real (not the declared) parameter type. Am I missing something?

Yes. Your expectation is wrong. In Java, dynamic method dispatch happens only for the object the method is called on, not for the parameter types of overloaded methods.

Citing the Java Language Specification:

When a method is invoked (§15.12), the number of actual arguments (and any explicit type arguments) and the compile-time types of the arguments are used, at compile time, to determine the signature of the method that will be invoked (§15.12.2). If the method that is to be invoked is an instance method, the actual method to be invoked will be determined at run time, using dynamic method lookup (§15.12.4).


As mentioned before overloading resolution is performed at compile time.

Java Puzzlers has a nice example for that:

Puzzle 46: The Case of the Confusing Constructor

This puzzle presents you with two Confusing constructors. The main method invokes a constructor, but which one? The program's output depends on the answer. What does the program print, or is it even legal?

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);
    }
}

Solution 46: Case of the Confusing Constructor

... Java's overload resolution process operates in two phases. The first phase selects all the methods or constructors that are accessible and applicable. The second phase selects the most specific of the methods or constructors selected in the first phase. One method or constructor is less specific than another if it can accept any parameters passed to the other [JLS 15.12.2.5].

In our program, both constructors are accessible and applicable. The constructor Confusing(Object) accepts any parameter passed to Confusing(double[]), so Confusing(Object) is less specific. (Every double array is an Object, but not every Object is a double array.) The most specific constructor is therefore Confusing(double[]), which explains the program's output.

This behavior makes sense if you pass a value of type double[]; it is counterintuitive if you pass null. The key to understanding this puzzle is that the test for which method or constructor is most specific does not use the actual parameters: the parameters appearing in the invocation. They are used only to determine which overloadings are applicable. Once the compiler determines which overloadings are applicable and accessible, it selects the most specific overloading, using only the formal parameters: the parameters appearing in the declaration.

To invoke the Confusing(Object) constructor with a null parameter, write new Confusing((Object)null). This ensures that only Confusing(Object) is applicable. More generally, to force the compiler to select a specific overloading, cast actual parameters to the declared types of the formal parameters.


Ability to dispatch a call to a method based on types of arguments is called multiple dispatch. In Java this is done with Visitor pattern.

However, since you're dealing with Integers and Strings, you cannot easily incorporate this pattern (you just cannot modify these classes). Thus, a giant switch on object run-time will be your weapon of choice.


In Java the method to call (as in which method signature to use) is determined at compile time, so it goes with the compile time type.

The typical pattern for working around this is to check the object type in the method with the Object signature and delegate to the method with a cast.

    public void foo(Object o) {
        if (o instanceof String) foo((String) o);
        if (o instanceof Integer) foo((Integer) o);
        logger.debug("foo(Object o)");
    }

If you have many types and this is unmanageable, then method overloading is probably not the right approach, rather the public method should just take Object and implement some kind of strategy pattern to delegate the appropriate handling per object type.


I had a similar issue with calling the right constructor of a class called "Parameter" that could take several basic Java types such as String, Integer, Boolean, Long, etc. Given an array of Objects, I want to convert them into an array of my Parameter objects by calling the most-specific constructor for each Object in the input array. I also wanted to define the constructor Parameter(Object o) that would throw an IllegalArgumentException. I of course found this method being invoked for every Object in my array.

The solution I used was to look up the constructor via reflection...

public Parameter[] convertObjectsToParameters(Object[] objArray) {
    Parameter[] paramArray = new Parameter[objArray.length];
    int i = 0;
    for (Object obj : objArray) {
        try {
            Constructor<Parameter> cons = Parameter.class.getConstructor(obj.getClass());
            paramArray[i++] = cons.newInstance(obj);
        } catch (Exception e) {
            throw new IllegalArgumentException("This method can't handle objects of type: " + obj.getClass(), e);
        }
    }
    return paramArray;
}

No ugly instanceof, switch statements, or visitor pattern required! :)