Это выводится foo(Object o) три раза. Я ожидаю, что при выборе метода будет учитываться реальный (а не объявленный) тип параметра. Я что-то упускаю? Есть ли способ изменить этот код так, чтобы он печатал foo(12), foo("foobar") и foo(Object o)?
Переведено автоматически
Ответ 1
Я ожидаю, что при выборе метода будет учитываться реальный (а не объявленный) тип параметра. Я что-то упускаю?
ДА. Ваши ожидания неверны. В Java динамическая отправка метода происходит только для объекта, на котором вызывается метод, а не для типов параметров перегруженных методов.
Когда вызывается метод (§15.12), количество фактических аргументов (и любых аргументов явного типа) и типы аргументов во время компиляции используются во время компиляции для определения сигнатуры метода, который будет вызван (§15.12.2). Если метод, который должен быть вызван, является методом экземпляра, фактический вызываемый метод будет определен во время выполнения с использованием динамического поиска метода (§ 15.12.4).
Ответ 2
Как упоминалось ранее, разрешение перегрузки выполняется во время компиляции.
Эта головоломка представляет вам два запутанных конструктора. Метод main вызывает конструктор, но какой именно? Результат программы зависит от ответа. Что программа печатает, или это вообще законно?
... Процесс разрешения перегрузки Java выполняется в два этапа. На первом этапе выбираются все доступные и применимые методы или конструкторы. На втором этапе выбирается наиболее специфичный из методов или конструкторов, выбранных на первом этапе. Один метод или конструктор менее специфичен, чем другой, если он может принимать любые параметры, переданные другому [JLS 15.12.2.5].
В нашей программе доступны и применимы оба конструктора. Конструктор Confusing(Object) принимает любой параметр, переданный в Confusing(double[]), поэтому Confusing(Object) менее специфичен. (Каждый двойной массив является объектом, но не каждый объект является двойным массивом.) Поэтому наиболее специфичный конструктор сбивает с толку (double[]), что объясняет вывод программы.
Такое поведение имеет смысл, если вы передаете значение типа double[]; оно противоречит интуиции, если вы передаете null. Ключом к пониманию этой головоломки является то, что тест, для которого метод или конструктор является наиболее специфичным, не использует фактические параметры: параметры, появляющиеся при вызове. Они используются только для определения того, какие перегрузки применимы. Как только компилятор определяет, какие перегрузки применимы и доступны, он выбирает наиболее конкретную перегрузку, используя только формальные параметры: параметры, указанные в объявлении.
Чтобы вызвать конструктор Confusing(Object) с параметром null, напишите new Confusing((Object)null). Это гарантирует, что применим только вводящий в заблуждение (объект). В более общем плане, чтобы заставить компилятор выбрать конкретную перегрузку, приведите фактические параметры к объявленным типам формальных параметров.
Однако, поскольку вы имеете дело с Integers и Strings , вы не можете легко включить этот шаблон (вы просто не можете изменять эти классы). Таким образом, гигантская switch нагрузка на объект во время выполнения будет вашим любимым оружием.
Ответ 4
В Java вызываемый метод (как и сигнатура метода, которую использовать) определяется во время компиляции, поэтому он соответствует типу во время компиляции.
Типичный способ обойти это - проверить тип объекта в методе с помощью сигнатуры объекта и делегировать методу с помощью приведения.
publicvoidfoo(Object o) { if (o instanceof String) foo((String) o); if (o instanceof Integer) foo((Integer) o); logger.debug("foo(Object o)"); }
Если у вас много типов, и это неуправляемо, то перегрузка метода, вероятно, неправильный подход, скорее открытый метод должен просто принимать Object и реализовывать какой-то шаблон стратегии для делегирования соответствующей обработки для каждого типа объекта.