Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Коллоквиум 2012.doc
Скачиваний:
25
Добавлен:
25.08.2019
Размер:
346.11 Кб
Скачать

7. Типизация. Правила преобразования типов. Instanceof и ClassCastException. Класс Class.

Java является строго типизированным языком, а это означает, что каждое выражение и каждая переменная имеет строго определенный тип уже на момент компиляции. Тип устанавливается на основе структуры применяемых выражений и типов литералов, переменных и методов, используемых в этих выражениях. При вычислении выражений аргументы оператора приводятся к одному типу, при этом более простой тип приводится к более сложному путем продвижения (byte-> short -> int ->long; float -> double; int->float или double).

В Java предусмотрено семь видов приведений: - тождественное (identity); - расширение примитивного типа (widening primitive); - сужение примитивного типа (narrowing primitive); - расширение объектного типа (widening reference); - сужение объектного типа (narrowing reference); - преобразование к строке (String); - запрещенные преобразования (forbidden).

Для простых типов расширение означает, что осуществляется переход от менее емкого типа к более емкому. Например, от типа byte (длина 1 байт) к типу int (длина 4 байта). Такие преобразования безопасны в том смысле, что новый тип всегда гарантированно вмещает в себя все данные, которые хранились в старом типе, и таким образом не происходит потери данных. Нельзя провести преобразование к типу char от типов меньшей или равной длины (byte, short), или, наоборот, к short от char без потери данных. Это связано с тем, что char, в отличие от остальных целочисленных типов, является беззнаковым. Даже при расширении данные все-таки могут быть в особых случаях искажены: приведение значений int к типу float и приведение значений типа long к типу float или double. Хотя эти дробные типы вмещают гораздо большие числа, чем соответствующие целые, но у них меньше значащих разрядов. Обратное преобразование - сужение - означает, что переход осуществляется от более емкого типа к менее емкому. При таком преобразовании есть риск потерять данные. В Java такое преобразование должно совершаться явным образом. Суж. явл-ся преобр-ния: - byte в char; - short в byte или char;  - char в byte или short; - int в byte, short, или char;  - long в byte, short, char, или int;  - float в byte, short, char, int, или long; - double в byte, short, char, int, long, или float. При сужении целочисленного типа к более узкому целочисленному все старшие биты, не попадающие в новый тип, просто отбрасываются. Не производится никакого округления или других действий для получения более корректного результата.

Расширяющие преобразования ссылок — это преобразования производных ссылочных типов в типы их предков, которые не требуют никаких действий на этапе исполнения и никогда не генерируют ошибок. Такими преобразованиями в Java являются: - преобразование любого класса или интерфейса в его предка (в частности, в тип Object); - преобразование класса в интерфейс, который он реализует; - преобразование любого массива в тип Object или тип Cloneable; - преобразование массива типа S[] в массив типа T[], если S и T — ссылочные типы, и преобразование S в T является расширяющим; - преобразование нулевого типа в любой ссылочной тип. Сужающие преобразования ссылок — это преобразования производных ссылочных типов в типы их потомков. Эти преобразования требуют проверки своей легитимности на этапе исполнения и могут генерировать исключение ClassCastException. Такими преобразованиями в Java являются: - преобразование любого класса в его потомка (в частности, преобразование типа Object в любой другой класс); - преобразование класса в интерфейс, когда класс не является финальным и не реализует данный интерфейс (в частности, преобразование типа Object в любой интерфейс); - преобразование типа Object в любой массив; - преобразование любого интерфейса в класс, который не является финальным; - преобразование любого интерфейса в класс, который является финальным и реализует данный интерфейс; - преобразование интерфейса J в интерфейс K, когда J не является потомком K, и не существует метода, декларированного и в J, и в K с одинаковой сигнатурой, но разными типами результата; - преобразование массива типа S[] в массив типа T[], если S и T — ссылочные типы, и преобразование S в T является сужающим.

Чтобы проверить, возможен ли желаемый переход, можно воспользоваться оператором instanceof. Существуют некоторые ограничения на использование instanceof: можно сравнивать только именованные типы, но не объекты Class.

Следующие преобразования типов в Java запрещены: - преобразование любого ссылочного типа в любой примитивный тип; - преобразование любого примитивного типа в любой ссылочной тип, кроме типа String; - преобразование нулевого типа в любой примитивный тип; - преобразования в нулевой тип или тип boolean; - преобразования типа boolean в любой другой тип, кроме типа String; - преобразование одного класса в другой, если ни один из них не является предком другого (кроме преобразования в тип String); - преобразование класса в интерфейс, если класс является финальным и не реализует данный интерфейс; - преобразование класса в массив, если класс отличен от Object; - преобразование интерфейс в класс, который является финальным и не реализует данный интерфейс (кроме преобразования в тип String); - преобразование интерфейса J в интерфейс K, если существует метод, декларированный и в J, и в K с одинаковой сигнатурой, но разными типами результата; - преобразование массива в класс, отличный от Object и String; - преобразование массива в интерфейс, отличный от Cloneable; - преобразование массива типа S[] в массив типа T[], если преобразование S в T является запрещенным

В Java реализована т.н. технология «отражения» (Java Reflection). В мире отражений все начинается с класса «java.lang.Class». Для каждого Java-класса или интерфейса, присутствующего в системе, существует экземпляр Class, содержащий подробную низкоуровневую информацию об этом классе. Например, с помощью этого экземпляра можно узнать списки членов класса – полей, методов, конструкторов, а также обратиться к этим членам. Кроме того, класс Class содержит ряд статических методов, обеспечивающих взаимодействие с внутренними механизмами Java, отвечающими за загрузку и управление классами. Сущ. два основыных способа получить экземпляр Class, соответствующий данному классу (или интерфейсу): - добавить к имени класса суффикс .class; - Если есть экземпляр некоторого класса, может быть даже неизвестного в данной точке программы, можно вызвать метод getClass(), присутствующий в каждом Java-объекте (унаследованный от класса Object). Примитивные типы Java – boolean, char, byte, short, int, long, float, double – обычно не считаются полноценными классами. Они не унаследованы от Object, для них не работает наследование и т.д. Тем не менее, с ними тоже ассоциированы экземпляры Class, которые можно получить первым способом. Существует специальный объект void.class – он используется в довольно экзотических ситуациях при вызове методов через отражения. Экземпляры типа Class есть также у любого массива. Располагая переменной типа Class для некоторого класса, можно получить полное имя класса методом getName. Можно проверить, не является ли класс интерфейсом, массивом или примитивным типом: методы isInterface(), isArray() и isPrimitive(). Если класс – массив, можно получить тип его элементов (т.е. соответствующий объект Class): метод getComponentType(). Для не-массивов этот метод вернет null.

Один из самых очевидных примеров использования класса Class – загрузка ресурсов. Ресурс в Java – это файл с данными, прилагаемый к вашей программе и обычно размещаемый «рядом» с class-файлами. Наиболее общий способ прочитать файл-ресурс произвольного формата, расположенный «рядом» с некоторым классом – обратиться к методу getResourceAsStream() объекта Class, соответствующего данному классу. В качестве параметра этому методу нужно передать путь к файлу ресурса относительно каталога, в котором размещен ваш класс. В качестве результата getResourceAsStream() вернет объект InputStream, с помощью которого можно прочитать файл ресурса стандартными средствами ввода/вывода Java. 

Классом Class реализуется динамическая загрузка произвольного класса по заданному имени.  Статический метод Class.forName отыскивает в системе (среди путей поиска классов CLASSPATH) класс с заданным именем className и возвращает соответствующий экземпляр класса Class. Имя className должно быть полным, т.е. включать имя пакета. Если такой класс отсутствует, возбуждается исключение ClassNotFoundException. После получения переменной типа Class, следующее наиболее типичное действие – создание экземпляра только что загруженного класса. Для этого служит метод Class.newInstance(). Чтобы этот метод мог выполниться, у загруженного класса должен существовать пустой конструктор (без аргументов), либо не должно быть описано вообще никаких конструкторов. В противном случае будет возбуждено исключение InstantiationException.

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