Главная » Java » Выбор метода Java

0

 

Чтобы вызов метода оказался корректным, необходимо передать методу верное количество аргументов предусмотренных типов – тогда в составе класса будет найден в точности один метод, соответствующий формату вызова. Если метод не перегружен, задача его отыскания упрощается, поскольку в классе существует только одно объявление метода с заданным именем. Поиск метода относительно несложен и тогда, когда уникально сочетание имени метода и набора его параметров.

Если несколько методов с одинаковым именем обладают сходными наборами параметров, выбор требуемой альтернативы существенно затрудняется. Чтобы определить верный метод, компилятор следует алгоритму поиска "наиболее близкого совпадения":

1)найти все методы, объявления которых сопоставимы с форматом Вызова, а именно все перегруженные методы с требуемым именем и такими параметрами, которые допускают присваивание значений переданных аргументов; если один из методов обеспечивает точное попарное совпадение типов параметров и значений аргументов, обратиться к этому методу;

2)если любой метод из набора "подходящих" методов обладает параметрами, типы которых могут быть присвоены типам соответствующих параметров другого метода, исключить этот "другой" метод из рассмотрения как менее подходящий; повторять операцию до тех пор, пока не будут выполнены все исключения;

3)если после выполнения инструкций п.2 остался только один метод, заслуживающий внимания, этот метод является наиболее подходящим и поэтому вызывается; если же в наборе "подходящих" осталось более одного метода, вызов следует считать неоднозначным (поскольку ему не отвечает в точности один наиболее подходящий метод) и потому неверным.

Теперь рассмотрим следующие варианты вызова методов чего_быСъесть: чего_быСъесть(ДесертСсылка, ПряникСсылка);

чего_быСъесть(ШоколадноеПирожноеСсылка, ДесертСсылка); чего_быСъесть(ШоколадноеПирожноеСсылка, МедовыйПряникСсылка); чего_быСъесть(ПирожноеСсылка, ПряникСсылка); // НЕВЕРНО!

 

Первый вызов соответствует первой форме метода чего_быСъесть, ПОСКОЛЬКУ значения аргументов в точности совпадают с типами параметров. Второй вызов отвечает второй форме, так как это единственная форма, параметрам которой могут быть присвоены значения передаваемых аргументов. В обоих случаях выбор метода Прост и осуществляется уже на первом шаге рассмотренного выше алгоритма.

Третий вызов требует некоторых размышлений. В список подходящих кандидатур попадают все три формы метода чего_быСъесть, поскольку аргумент ШоколадноеПирожноеСсылка может быть присвоен любому из первых параметров, а аргумент МедовыйПряникСсылка – любому из вторых, причем ни одна из форм не соответствует вызову точно. Итак, по завершении первого шага алгоритма в нашем распоряжении остаются три альтернативы.

Выполняя второй шаг алгоритма, мы должны исключить из дальнейшего рассмотрения те методы, которые являются менее подходящими. В нашем случае надлежит исключить первую форму метода чего_быСъесть, поскольку третью следует считать более подходящей – ссылка на любой объект класса ШоколадноеПирожное может быть присвоена параметру типа десерт первой формы и ссылка на объект Пряник, естественно, допускает присваивание параметру типа пряник первой формы, поэтому первая форма нам подходит меньше. Вторая форма исключается из рассмотрения на основании аналогичных выводов. Теперь множество альтернатив сведено до одной, третьей формы чего_быСъесть, которая и должна быть вызвана.

Четвертый вызов неверен. После выполнения первого шага алгоритма во множество подходящих включаются первая и вторая формы чего_быСъесть. Поскольку ни один из наборов типов параметров не допускает присваивания другому набору типов, при выполнении второго шага алгоритма ни одна из двух форм не может быть исключена из рассмотрения. Следовательно, вызов двусмыслен и не может быть удовлетворительно разрешен компилятором, поэтому есть все основания говорить о его некорректности. Исправить ситуацию поможет оператор явного преобразования одного из аргументов к типу десерт: если преобразуется аргумент ПирожноеСсылка, будет вызвана первая форма чего_быСъесть, а если выполнить преобразование аргумента ПряникСсылка, то подходящей окажется вторая форма.

Рассмотренные правила применимы также и к простым типам. Значения типа int, например, могут присваиваться, параметрам float, и при разрешении коллизий, связанных с поиском подходящего перегруженного метода, это обстоятельство следует принимать в расчет – точно так же, как и возможность присваивания ссылки на объект класса МедовыйПряник параметру типа пряник. Впрочем, к неявным преобразованиям значений широких целочисленных типов в более узкие эта схема неприменима: если метод обладает параметром тип short, а ему передается аргумент int, следует выполнить явное преобразование int в short, иначе совпадения аргумента int – независимо от его значения -с параметром short достичь не удастся.

 

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

 

Методы не должны различаться только типом возвращаемого значения или списком объявляемых исключений, поскольку степень неопределенности выбора в такой ситуации становится непозволительно высокой. Если, например, существуют два метода с именем doppe1 ganger, которые различаются только тем, что один возвращает значение типа int, а другой – short, в контексте следующего выражения "подходящими" окажутся оба:

double d = doppelganger();

 

Аналогичные проблемы сопутствуют и коду, в котором используются методы, различающиеся только списками объявляемых исключений, – код, из которого выполняется обращение к методу, возможно, займется "отловом" некоторых или всех исключений, генерируемых методом, либо не будет реагировать на них вовсе. В такой ситуации надежного способа определения наиболее подходящего метода просто не существует.

Подобные недоразумения не всегда удается выявить на этапе компиляции.

Например, базовый класс может быть пополнен новым методом, который отличается от метода, существующего в производном классе, только типом возвращаемого значения. Если код производного класса не был заново откомпилирован, ошибка останется незамеченной. На этапе выполнения программы все протекает по-старому – точная форма метода, подлежащего вызову, была определена на этапе компиляции, и поэтому система успешно отыщет его в производном классе, даже не "подумав" обратиться к базовому. Аналогично, если в класс была добавлена новая перегруженная форма метода, но внешняя программа, использующая этот класс, заново не компилировалась, новый метод так и останется невостребованным.

 

Источник: Арнолд, Кен, Гослинг, Джеймс, Холмс, Дэвид. Язык программирования Java. 3-е изд .. : Пер. с англ. – М. : Издательский дом «Вильяме», 2001. – 624 с. : ил. – Парал. тит. англ.

По теме:

  • Комментарии