Как Java выбирает какую перегруженную функцию вызывать? - PullRequest
8 голосов
/ 22 декабря 2008

Это чисто теоретический вопрос.

Дано три простых класса:

class Base {
}

class Sub extends Base {
}    

class SubSub extends Sub {
}

И функция, предназначенная для работы с этими классами:

public static void doSomething(Base b) {
  System.out.println("BASE CALLED");
}
public static void doSomething(Sub b) {
  System.out.println("SUB CALLED");
}

Кажется, что следующий код:

SubSub ss = new SubSub();
doSomething(ss);

может законно привести к печати BASE CALLED или SUB CALLED, поскольку SubSub может быть приведен к обоим из них. Фактически, удаление Sub-версии функции приводит к печати BASE CALLED. На самом деле происходит то, что «SUB CALLED» печатается. Похоже, это означает, что то, какая функция вызывается, не зависит от порядка, в котором определены функции, так как базовая версия была вызвана первой.

Java просто просматривает все различные версии функции и выбирает ту, которая требует наименьшего обхода стека наследования? Это стандартизировано? Это записано в какой-либо документации?

Ответы [ 4 ]

8 голосов
/ 22 декабря 2008

Формальную спецификацию можно найти в части 15.12.2.5 Спецификации языка Java (JLS) . Благодаря генерикам это довольно сложно, поэтому вы можете посмотреть тот же раздел первого издания JLS .

В основном это говорит о том, что компилятор пытается найти версию метода, где все параметры, включая объект, к которому вызывается метод, являются наиболее конкретными. Если такого метода не существует (например, если у вас есть method(Base, Sub) и method(Sub, Base), но не method(Sub, Sub)), то компиляция завершится неудачей.

Обратите внимание, что фактический выбор метода зависит от динамического типа целевого объекта для методов экземпляра, но не от параметров. Ваш пример все равно будет работать на уровне экземпляра.

Вы должны быть в состоянии помочь компилятору протянуть руку помощи, применив или повторно указав тип ss. Если объявленный тип переменной точно соответствует сигнатуре, то все понятно и программистам компилятора и обслуживающего персонала. Не имеет значения, если вы назначите более конкретный тип, если объявленный тип совпадает.

2 голосов
/ 22 декабря 2008

Насколько я знаю, Java и C ++ принимают это решение во время компиляции (так как это статические функции, которые не могут быть динамически диспетчеризируемыми) на основе самого конкретного соответствия, которое они могут выполнить. Если ваш статический тип - SubSub, и у вас есть перегрузка, которая принимает SubSub, то это тот, который будет вызван. Я уверен, что это в обоих стандартах.

Если у вас есть ссылка или указатель на Base, даже если она содержит Sub или SubSub, вы будете соответствовать версии, которая принимает Base, потому что во время компиляции это единственная гарантия, которую имеет компилятор.

0 голосов
/ 20 июля 2014

Java связывает методы динамически (во время выполнения, в зависимости от типа экземпляра объекта, а не ссылочного типа), но только в контексте одной сигнатуры метода. Java связывает сигнатуру метода статически (во время компиляции).
Другими словами , Java принимает решение о том, какой метод (подпись) следует вызывать во время компиляции (перегрузка статически - на основе ссылок). Во время выполнения Java возьмет эту сигнатуру, найдет нужный объект в иерархии типов объектов и выполнит этот метод для этого динамически связанного объекта.

Перегрузка -> что (сигнатура метода во время компиляции)
Переопределение -> откуда (объект в иерархии типов во время выполнения)

0 голосов
/ 22 декабря 2008

Когда вы перегружаете статические методы, он вызывает тот метод, который определен непосредственно в классе, который вызывает метод. Однако, если метод не определен в вызывающем классе, он вызовет метод, унаследованный от его непосредственного родительского класса.

В вашем случае есть два перегруженных метода, каждый из которых может принимать SubSub в качестве параметра. компилятор проверяет наиболее точное совпадение и выполняет его. Но самое конкретное совпадение, как правило, самое низкое в иерархии типов.

EDITED

Удален конфликтующий оператор. Два метода в классах, которые находятся на одном уровне иерархии типов, не могут быть в неоднозначном состоянии для выбора компилятором. Эта двусмысленность возможна только в случае множественного наследования.

...