Разрешение перегрузки метода в java

Вот что я знаю о разрешении перегрузки в java:


Процесс компилятора, пытающегося разрешить вызов метода из заданного перегруженные определения методов называются разрешением перегрузки. Если компилятор не может найти точное совпадение, он ищет ближайшее совпадение используя только upcasts (downcasts никогда не выполняются).


Вот класс:

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

}

Как и ожидалось, выход равен 10.

Однако, если я немного изменил определение класса и изменил второй перегруженный метод.

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

}

Выход: 8.

Здесь я смущен. Если downcasting никогда не использовалось, то почему 8 печатались вообще? Почему компилятор взял метод TestOverLoad, который принимает int как аргумент, который является downcast от Integer к int?

Ответ 1

Компилятор будет рассматривать не понижающее, а преобразование unboxing для разрешения перегрузки. Здесь Integer i будет успешно распакован в int. Метод String не учитывается, потому что Integer нельзя расширить до String. Единственная возможная перегрузка - это тот, который считает unboxing, поэтому 8 печатается.

Причина, по которой первый вывод кода 10 заключается в том, что компилятор рассмотрит расширение ссылочного преобразования (Integer - Object) по преобразованию unboxing.

В разделе 15.12.2 JLS при рассмотрении того, какие методы применимы, говорится:

  • Первая фаза (§15.12.2.2) выполняет разрешение перегрузки без разрешения преобразования бокса или распаковки или использования вызова метода переменной arity. Если на этом этапе не обнаружен какой-либо применимый метод, обработка продолжается до второй фазы.

 

  1. Вторая фаза (§15.12.2.3) выполняет разрешение перегрузки при разрешении бокса и распаковки [...]

Ответ 2

В Java методы решения в случае перегрузки метода выполняются со следующим приоритетом:

1. Расширяя
 2. Авто-бокс
 3. Вар-арг

Компилятор java считает, что расширение примитивного параметра более желательно, чем выполнение операции автоматического бокса.

Другими словами, когда в Java 5 был введен автоматический бокс, компилятор выбирает более старый стиль (расширение), прежде чем он выберет более новый стиль ( авто-бокс), сохраняя существующий код более надежным. То же самое с var-args.

В вашем 1-м фрагменте кода расширение ссылочной переменной происходит, т.е. Integer до Object, а не un-boxing i.e, Integer до int. И во втором фрагменте расширения не может быть от Integer до String, поэтому произойдет распаковка.

Рассмотрим приведенную ниже программу, которая доказывает все приведенные выше утверждения:

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

Вывод:

double double double double int,int Long,Long

Просто для справки, вот мой блог о перегрузке метода в Java.

P.S: Мой ответ - это модифицированная версия примера, приведенного в SCJP.

Ответ 3

Фактически во втором примере не происходит нисходящего перехода. Произошло следующее:

1. Целое число распаковано/распаковано на примитивный тип int.
2. Затем вызывается метод TestOverLoad(int a).

В основном методе вы объявляете Integer как -

 Integer i = 9;  

Затем вызов -

test.TestOverLoad(i);  

В то время как у вас есть 2 перегруженная версия TestOverLoad() -

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

Здесь вторая перегруженная версия TestOverLoad() принимает совершенно другой аргумент String. Вот почему Integer i распаковывается в примитивный тип int, после чего вызывается первая перегруженная версия.

Ответ 4

расширение бьет бокс, бокс бьет var-args. В вашем примере расширение не может произойти, поэтому применяемый бокс и Integer не распаковываются. Ничего необычного.

Ответ 5

Все объекты Java расширяют класс Object, включая класс Integer. Эти два класса имеют следующее отношение: Integer "является (n)" Object, потому что Integer расширяет Object. В первом примере используется метод с параметром Object.

Во втором примере методы не найдены, которые принимают Integer. В этом случае Java использует то, что называется auto-unboxing, чтобы разрешить класс-оболочку Integer с примитивным int. Таким образом, используется метод с параметром int.