Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
GoslingJava2.doc
Скачиваний:
140
Добавлен:
23.02.2016
Размер:
2.39 Mб
Скачать

5.13.3. Строковое приведение

Класс String отличается от остальных: это неявно используется в операторе конкатенации +, а строковые литералы ссылаются на объекты String. Примеры нам уже встречались в программах: при выполнении конкатенации Java пытается преобразовать в String все, что еще не относится к этому типу. Подобные приведения определены для всех примитивных типов и осуществляются вызовом метода toString объекта (см. раздел “Метод toString).

Если преобразовать в String пустую ссылку, то результатом будет строка “null”. Если для данного класса метод toString не определен, то используется метод, унаследованный от класса Object и возвращающий строковое представление типа объекта.

5.14. Доступ к членам

Доступ к членам объекта осуществляется с помощью оператора .— например, obj.method(). Оператор . может применяться и для доступа к статическим членам либо по имени класса, либо по ссылке на объект. Если для доступа к статическим членам используется ссылка на объект, то выбор класса осуществляется на основании объявленного типа ссылки, а не фактического типа объекта. Длядоступа к элементам массивов служат квадратные скобки— например, array[i].

Если использовать . или [] со ссылкой, значение которой равно null, то возбуждается исключение NullPointerException (кроме того случая, когда вы используете . для вызова статического метода). Если индекс массива выходит за его пределы, возбуждается исключение IndexOutOfBounds. Проверка осуществляется при каждом обращении к элементу массива. /По крайней мере runtime-система ведет себя именно так, но компилятор часто может избежать проверки в тех случаях, когда он действительно уверен, что все в порядке, например, если значение переменной цикла всегда лежит в допустимом диапазоне./

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

Если существует два и более перегруженных метода с одинаковым количеством параметров, выбор правильной версии становится несколько более сложным. Для этого Java пользуется следующим алгоритмом, называемым “алгоритмом самого точного совпадения”:

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

2. У нас остался список методов, каждый из которых подходит для вызова. Проделаем следующую процедуру. Рассмотрим первый метод в списке. Если вместо параметров в первом методе могут быть использованы параметры еще какого-либо метода из списка— исключим первый метод из списка. Эта процедура повторяется до тех пор, пока остается возможность удаления методов.

3. Если остался ровно один метод, то совпадение для него оказывается самым точным, и он будет вызываться в программе. Если же осталось несколько методов, то вызов является неоднозначным; точного совпадения не существует, а вызов оказывается недопустимым.

Например, предположим, что у нас имеется усовершенствованная версия класса с десертами из раздела3.2:

Допустим также, что у нас имеется несколько перегруженных методов, которые вызываются для конкретных комбинаций параметров Dessert:

void moorge(Dessert d, Scone s) { /* Первая форма */ }

void moorge(Cake c, Dessert d) { /* Вторая форма */ }

void moorge(ChocolateCake cc,Scone s) { /* Третья форма */ }

Теперь рассмотрим следующие вызовы moorge:

moorge(dessertRef, sconeRef);

moorge(chocolateCakeRef, dessertRef);

moorge(chocolateCakeRef, butteredSconeRef);

moorge(cakeRef, sconeRef); // НЕВЕРНО

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

С третьим вызовом дело обстоит сложнее. Список потенциальных кандидатов включает все три формы, потому что ссылка chocolateCakeRef может быть присвоена первому параметру, а ButteredScone— второму параметру во всех трех формах, и ни для одной из сигнатур не находится точного совпадения. Следовательно, после шага 1 у нас имеется набор из трех методов-кандидатов.

На шаге 2 из набора исключаются все методы с менее точным совпадением. В нашем случае первая форма исключается из-за того, что совпадение для третьей формы оказывается более точным. Действительно, рассмотрим третий и первый методы. Ссылка на ChocolateCake (из третьей формы) может быть присвоена параметру типа Desert (из первой формы), а ссылка на Scone (из третьей формы) непосредственно присваивается параметру типа Scone (в первой форме). Вторая форма исключается из набора по аналогичным соображениям. В итоге количество возможных методов сократилось до одного (третьей формы moorge), и именно этот метод и будет вызван.

Последний вызов является недопустимым. После шага 1 набор возможных методов состоит из первой и второй форм. Поскольку параметры любой из этих форм не могут быть присвоены параметрам другой, на шаге 2 не удается исключить из набора ни одну из этих форм. Следовательно, мы имеем дело с неоднозначным вызовом, по поводу которого компилятор не может принять никакого решения, что является недопустимым.

Эти же правила относятся и к примитивным типам. Например, значение int может быть присвоено переменной float, и при рассмотрении перегруженного вызова этот факт будет принят во внимание— точно так же, как и возможность присваивания ссылки ButteredScone ссылке Scone.

Перегруженные методы не могут отличаться одним лишь типом возвращаемого значения и/или списком возбуждаемых исключений, поскольку в противном случае при выборе запускаемого метода возникало бы слишком много неоднозначностей. Например, если бы существовало два метода doppelgänger, которые бы отличались только тем, что один из них возвращает int, а другой— short, то отдать предпочтение одному из них в следующем операторе было бы невозможно:

double d = doppelgänger();

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]