Я экспериментирую с этим кодом:
interface Callee {
public void foo(Object o);
public void foo(String s);
public void foo(Integer i);
}
class CalleeImpl implements Callee
public void foo(Object o) {
logger.debug("foo(Object o)");
}
public void foo(String s) {
logger.debug("foo(\"" + s + "\")");
}
public void foo(Integer i) {
logger.debug("foo(" + i + ")");
}
}
Callee callee = new CalleeImpl();
Object i = new Integer(12);
Object s = "foobar";
Object o = new Object();
callee.foo(i);
callee.foo(s);
callee.foo(o);
Это печатается foo(Object o)
три раза. Я ожидаю, что при выборе метода будет учитываться реальный (а не заявленный) тип параметра. Я что-то упускаю? Есть ли способ изменить этот код, чтобы он печатал foo(12)
, foo("foobar")
и foo(Object o)
?
foo(Object)
. Во время выполнения класс объекта, для которого вызывается метод, определяет, какая реализация этого метода вызывается, с учетом того, что это может быть экземпляр подкласса объявленного типа, который переопределяет метод.Как упоминалось ранее, разрешение перегрузки выполняется во время компиляции.
У Java Puzzlers есть хороший пример для этого:
Головоломка 46: Дело о сбивающем с толку конструкторе
Эта головоломка представляет вам двух сбивающих с толку конструкторов. Основной метод вызывает конструктор, но какой? Результат работы программы зависит от ответа. Что печатает программа, и это вообще законно?
Решение 46: случай сбивающего с толку конструктора
... Процесс разрешения перегрузки Java состоит из двух этапов. На первом этапе выбираются все доступные и применимые методы или конструкторы. На втором этапе выбирается наиболее конкретный из методов или конструкторов, выбранных на первом этапе. Один метод или конструктор менее специфичен, чем другой, если он может принимать любые параметры, переданные другому [JLS 15.12.2.5].
В нашей программе оба конструктора доступны и применимы. Конструктор Confusing (Object) принимает любой параметр, переданный в Confusing (double []) , поэтому Confusing (Object) менее конкретен. (Каждый двойной массив является объектом , но не каждый объект является двойным массивом .) Поэтому наиболее конкретным конструктором является Confusing (double []) , который объясняет вывод программы.
Такое поведение имеет смысл, если вы передаете значение типа double [] ; это нелогично, если вы передадите null . Ключ к пониманию этой загадки заключается в том, что тест, для которого метод или конструктор наиболее специфичен, не использует фактические параметры : параметры, появляющиеся в вызове. Они используются только для определения возможных перегрузок. Как только компилятор определяет, какие перегрузки применимы и доступны, он выбирает наиболее конкретную перегрузку, используя только формальные параметры: параметры, указанные в объявлении.
Чтобы вызвать конструктор Confusing (Object) с параметром null , напишите new Confusing ((Object) null) . Это гарантирует, что применим только Confusing (Object) . В более общем смысле, чтобы заставить компилятор выбрать конкретную перегрузку, приведите фактические параметры к объявленным типам формальных параметров.
источник
Возможность отправки вызова метода на основе типов аргументов называется многократной отправкой . В Java это делается с помощью шаблона Visitor .
Однако, поскольку вы имеете дело с
Integer
s иString
s, вы не можете легко включить этот шаблон (вы просто не можете изменять эти классы). Таким образом,switch
вашим любимым оружием будет гигант во время выполнения объекта.источник
В Java метод для вызова (как и подпись метода) определяется во время компиляции, поэтому он соответствует типу времени компиляции.
Типичный шаблон для решения этой проблемы - это проверка типа объекта в методе с помощью сигнатуры объекта и делегирование методу с приведением.
Если у вас много типов и это неуправляемо, то перегрузка метода, вероятно, не правильный подход, скорее общедоступный метод должен просто взять Object и реализовать какой-то шаблон стратегии для делегирования соответствующей обработки для каждого типа объекта.
источник
У меня была аналогичная проблема с вызовом правильного конструктора класса под названием «Parameter», который мог принимать несколько основных типов Java, таких как String, Integer, Boolean, Long и т. Д. Учитывая массив объектов, я хочу преобразовать их в массив моих объектов Parameter, вызывая наиболее точный конструктор для каждого объекта во входном массиве. Я также хотел определить конструктор Parameter (Object o), который генерирует исключение IllegalArgumentException. Я, конечно, обнаружил, что этот метод вызывается для каждого объекта в моем массиве.
Решение, которое я использовал, заключалось в том, чтобы найти конструктор через отражение ...
Никаких уродливых instanceof, операторов switch или шаблона посетителя не требуется! :)
источник
Java смотрит на ссылочный тип при попытке определить, какой метод вызвать. Если вы хотите принудительно использовать свой код, вы выбираете «правильный» метод, вы можете объявить свои поля как экземпляры определенного типа:
Вы также можете указать свои параметры как тип параметра:
источник
Если есть точное совпадение между количеством и типами аргументов, указанных в вызове метода, и сигнатурой метода перегруженного метода, то будет вызван именно этот метод. Вы используете ссылки на объекты, поэтому java решает во время компиляции, что для параметра объекта существует метод, который принимает объект напрямую. Он вызвал этот метод 3 раза.
источник