Главная » Java » Совместимость и преобразование типов

0

Java относится к категории строго типизированных языков программирования – это означает, что проверка совместимости типов, препятствующая выполнению любых сомнительных операций преобразования и присваивания, в большинстве случаев осуществляется на этапе компиляции. Теперь, когда вы осведомлены об основных особенностях взаимоотношений базовых и производных классов, уместно вновь вернуться к некоторым вопросам совместимости ссылочных типов внутри выражений присваивания (явного или косвенного) и допустимости взаимных преобразований между типами. Проблемы совместимости и преобразований простых типов рассмотрены в разделе 6.8 на странице 185.

Совместимость

В любой ситуации, когда значение выражения присваивается некоторой переменной – в процессе инициализации, в предложении присваивания или при передаче аргумента с последующим неявным присваиванием его параметру метода, – тип выражения должен быть совместим (compatible) с типом переменной. Если говорить о ссылочных типах, это означает, что выражение должно относиться к тому же классу, что и переменная, либо к соответствующему производному классу. Например, любой метод, для которого предусмотрена передача аргумента типа Attr, способен воспринять и аргумент типа ColorAttr, поскольку ColorAttr является производным классом базового класса Attr. Подобное принято называть совместимостью присваивания (assignment compatibility). Но обратное утверждение не верно – вам не удастся ни явно присвоить значение типа Attr переменной класса colorAttr, ни передать аргумент типа Attr методу, в котором объявлен параметр класса ColorAttr.

Те же правила действуют и в отношении выражений, которые возвращаются из тела метода командой return, – тип конкретного возвращаемого выражения должен быть совместим с тем, который значится в Объявлении метода.

Значение null – это специальный случай: null позволено присваивать переменным всех ссылочных типов, включая и массивы.

О типах, находящихся на более высоких ступеньках иерархии классов, говорят как о более широких, или менее конкретных, по сравнению с теми, которые расположены ниже. Соответственно производные типы называют более узкими, или более конкретными, нежели их "прародители". Когда в выражении, предусматривающем использование объекта базового класса, при меняется объект производного класса, имеет место преобразование с расширением типа. Подобная операция, допустимость которой проверяется на этапе компиляции, приводит к тому, что объект производного типа интерпретируется программой как объект базового класса. Программисту в этом случае не нужно предпринимать никаких дополнительных усилий. Обратное действие, когда ссылка на объект базового класса преобразуется в ссылку на объект производного класса, называют преобразованием с сужением типа. В этом случае следует явно применить оператор преобразования (casting) типов.

Явное преобразование типов

Оператор преобразования типов позволяет сообщить компилятору, что конкретное выражение следует трактовать таким образом, будто оно относится к тому типу, который явно указан. Оператор, формально говоря, может быть применен в любых ситуациях, но, его обычное употребление связано все-таки с преобразованиями, предусматривающими сужение типа. Оператор преобразования типов представляет собой конструкцию, состоящую из наименования требуемого Типа, заключенного в круглые скобки, которую размещают непосредственно перед выражением, подлежащим преобразованию. В предыдущем примере, а именно в теле метода ргintMN, мы уже использовали выражение преобразования с расширением типа:

           

            That serf = (That)this;

Хотя эта попытка преобразования оказалась бесполезной и безуспешной, мы, тем не менее, еще не предвидя окончательного результата, точно обозначили свои намерения – необходимость интерпретации текущего объекта в виде экземпляра базового класса. Если бы затем мы захотели присвоить sref обратно некоторой ссылочной переменной mref более узкого типа More, наличие оператора преобразования типов оказалось бы существенным:

 

More mref = (More)sref;

 

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

Преобразование с расширением типа нередко обозначают терминами nреобразование вверх (upcasting), или безопасное преобразование, поскольку тип, расположенный на низкой ступени иерархии, при водится к классу более высокого уровня, а подобная операция заведомо допустима. Преобразование с сужением типа соответственно называют nреобразоваиuем вниз (downcasting) – воздействию подвергается объект базового типа, который должен быть интерпретирован как объект производного типа. Преобразование вниз – это еще и небезопасное преобразование, поскольку его результат в общем случае может быть неверен.

Когда компилятор, анализируя исходный текст программы, встречает Выражение явного преобразования типов, он всегда проявляет бдительность, не "веря" тому, что операция корректна. Если подозрения оправдываются еще На этапе компиляции, выдается соответствующее сообщение об ошибке. Если же компилятор не в состоянии сразу подтвердить возможность преобразования или опровергнуть ее, он добавляет в код дополнительные инструкции, призванные проверить код во время его выполнения. Когда подобный контроль дает Отрицательный результат, генерируется исключение типа ClassCastExeption.

Проверка типа

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

 

if (sref instanceof Mоге)

 mref = (More)sref;

 

Заметьте, мы все еще обязаны при менять оператор преобразования типов, чтобы сообщить компилятору о наших истинных намерениях.

Конструкция проверки типа с помощью оператора instanceof особенно полезна в тех ситуациях, когда методу, как правило, не требуется передавать аргумент более широкого типа, но если подобное все-таки происходит, метод обязан отреагировать должным образом. Скажем, некий метод сортировки sort, как предусмотрено в его объявлении, обычно работает с объектом класса List, но если в качестве аргумента передается ссылка на объект SortedList, который уже отсортирован, делать далее уже ничего не нужно:

 

public static void sort(List list) {

       if (list instanceof SortedList) return;   // Все уже готово

еlsе

// сортировка списка

}

 

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

По теме:

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