简体   繁体   中英

Method overload resolution in java

Here is what I know about overload resolution in java:


The process of compiler trying to resolve the method call from given overloaded method definitions is called overload resolution. If the compiler can not find the exact match it looks for the closest match by using upcasts only (downcasts are never done).


Here is a class:

public class MyTest {

    public static void main(String[] args) {
        MyTest test = new MyTest();
        Integer i = 9;
        test.TestOverLoad(i);
    }

    void TestOverLoad(int a){
        System.out.println(8);
    }

    void TestOverLoad(Object a){
        System.out.println(10);
    }

}

As expected the output is 10.

However if I change the class definition slightly and change the second overloaded method.

public class MyTest {

    public static void main(String[] args) {
        MyTest test = new MyTest();
        Integer i = 9;
        test.TestOverLoad(i);
    }

    void TestOverLoad(int a){
        System.out.println(8);
    }

    void TestOverLoad(String a){
        System.out.println(10);
    }

}

The output is 8.

Here I am confused. If downcasting was never to be used, then why did 8 get printed at all? Why did compiler pick up the TestOverLoad method which takes int as an argument which is a downcast from Integer to int ?

The compiler will consider not a downcast, but an unboxing conversion for overload resolution. Here, the Integer i will be unboxed to an int successfully. The String method isn't considered because an Integer cannot be widened to a String . The only possible overload is the one that considers unboxing, so 8 is printed.

The reason that the first code's output is 10 is that the compiler will consider a widening reference conversion ( Integer to Object ) over an unboxing conversion.

Section 15.12.2 of the JLS , when considering which methods are applicable, states:

  1. The first phase (§15.12.2.2) performs overload resolution without permitting boxing or unboxing conversion, or the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the second phase.

  1. The second phase (§15.12.2.3) performs overload resolution while allowing boxing and unboxing [...]

In Java, resolving methods in case of method overloading is done with the following precedence:

1. Widening
2. Auto-boxing
3. Var-args

The java compiler thinks that widening a primitive parameter is more desirable than performing an auto-boxing operation.

In other words, as auto-boxing was introduced in Java 5, the compiler chooses the older style( widening ) before it chooses the newer style( auto-boxing ), keeping existing code more robust. Same is with var-args .

In your 1st code snippet, widening of reference variable occurs ie, Integer to Object rather than un-boxing ie, Integer to int . And in your 2nd snippet, widening cannot happen from Integer to String so unboxing happens.

Consider the below program which proves all the above statements:

class MethodOverloading {

    static void go(Long x) {
        System.out.print("Long ");
    }

    static void go(double x) {
        System.out.print("double ");
    }

    static void go(Double x) {
        System.out.print("Double ");
    }

    static void go(int x, int y) {
        System.out.print("int,int ");
    }

    static void go(byte... x) {
        System.out.print("byte... ");
    }

    static void go(Long x, Long y) {
        System.out.print("Long,Long ");
    }

    static void go(long... x) {
        System.out.print("long... ");
    }

    public static void main(String[] args) {
        byte b = 5;
        short s = 5;
        long l = 5;
        float f = 5.0f;
        // widening beats autoboxing
        go(b);
        go(s);
        go(l);
        go(f);
        // widening beats var-args
        go(b, b);
        // auto-boxing beats var-args
        go(l, l);
    }
}

The output is:

double double double double int,int Long,Long

Just for reference, here is my blog on method overloading in Java .

PS: My answer is a modified version of an example given in SCJP.

widening beats boxing, boxing beats var-args. In your example, the widening cannot happen, so the boxing it's applied and Integer is unboxed. Nothing unordinary.

Actually in the second example no downcasting is occurred. There occurred the following thing -

1. Integer is unwrapped/unboxed to primitive type int .
2. Then the TestOverLoad(int a) method is called.

In main method you declare Integer like -

 Integer i = 9;  

Then call -

test.TestOverLoad(i);  

Whereas, you have 2 overloaded version of TestOverLoad() -

TestOverLoad(int a); 
TestOverLoad(String a);

Here the second overloaded version of TestOverLoad() takes completely different argument String . Thats why Integer i is unboxed to a primitive type int and after that the first overloaded version is called.

All objects in Java extend the class Object, including the class Integer. These two class have the following relationship: Integer "is a(n)" Object because Integer extends Object. In your first example, the method with Object parameter is used.

In the second example, no methods are found that accept an Integer. In this case Java uses what is called auto-unboxing to resolve the Integer wrapper class to a primitive int. Thus, the method with the int parameter is used.

While accepted answer of @rgettman leads to very right source, more precisely, §15.12.2.2 and §15.12.2.3 of the JLS Section 15.12.2 discuss the applicability , not the resolution - what the OP asked for. In the example the OP provided both testOverLoad methods are applicable , .ie will be successfully resolved in the absence of another one. Instead, 15.12.2.5. Choosing the Most Specific Method discusses the resolution of the applicable methods. It reads:

One applicable method m1 is more specific than another applicable method m2, for an invocation with argument expressions e1, ..., ek, if any of the following are true: ...

m1 and m2 are applicable by strict or loose invocation, and where m1 has formal parameter types S1, ..., Sn and m2 has formal parameter types T1, ..., Tn, the type Si is more specific than Ti for argument ei for all i (1 ≤ i ≤ n, n = k).

So, in the first example, provided by OP, for parameter i of type Integer method testOverLoad(Object a) is more specific than testOverLoad(int a) .

This is happening due to Widening and Narrowing Type casting

Widening means a small type can be accommodated in a larger type without any loss of information. Widening Typecasting is automatic. That means a byte value can be automatically casted to short, int, long or double.

byte->short->int->float->double Widens from left to right.

Type Casting in Java

Hope this answers your question!

you can check with one more example :

public class HelloWorld {

void show(String c){
    System.out.println("int double overloaded method");
}

 void show(Object c){
    System.out.println("double int overloaded method");
}

}

here the you will get : double int overloaded method

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM