Метод называется перегруженным если в одном классе определено несколько методов, которые имеют одно названия, но разный набор параметров. Выбор метода, который нужно запустить в каждом конкретном случае, выполняется на этапе компиляции.
Выбор нужного метода осуществляется за два шага. Последовательность поиска на первом этапе:
- в первую очередь выбираются методы, подходящие по типу, в том числе учитываются примитивные и ссылочные типы;
- если на первом этапе выбрать нужный метод не удалось, то учитывается возможность boxing/ unboxing;
- если и на втором этапе не нашлось требуемого метода, то поиск продолжается с учётом vararg-параметров.
В случае, если требуемый метод не нашёлся, компиляция завершается с ошибкой.
Если при поиске на определённой фазе, находится сразу несколько пригодных методов, выбирается наиболее специфичный. Из двух методов более специфичным считается тот, у которого типы параметров являются подтипами сравниваемого с ним. Таким образом аргументы метода, определённого как более специфичный, подойдут для другого метода, а наоборот нет.
// Сигнатуры foo в порядке убывания приоритета
void foo(long a, Object b) {} // Фаза 1: только upcasting по обоим параметрам
void foo(Integer a, Object b){} // Фаза2: для первого параметра понадобится боксинг
void foo(int a, Object... b) {} // Фаза 3: vararg
void foo(long a, Object... b) {} // Фаза 3:тоже vararg, но метод менее специфичный - long шире чем int
void bar(Integer a, int b) {}
void bar(Integer a, Object b) {}
void baz(Object a, Long b) {}
void test () {
foo((int) 42, "Это будет Object"); // Эти параметры удовлетворяют всем трём фазам - используется первая.
bar(1, 2); // Оба метода из фазы 2 (нужен боксинг для первого аргумента), int не подтип Object.
bar(l, 2); // int -> Integer -> Object работает, int -> long -> Long не работает.
}