русс | укр

Языки программирования

ПаскальСиАссемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование

Все о программировании


Linux Unix Алгоритмические языки Аналоговые и гибридные вычислительные устройства Архитектура микроконтроллеров Введение в разработку распределенных информационных систем Введение в численные методы Дискретная математика Информационное обслуживание пользователей Информация и моделирование в управлении производством Компьютерная графика Математическое и компьютерное моделирование Моделирование Нейрокомпьютеры Проектирование программ диагностики компьютерных систем и сетей Проектирование системных программ Системы счисления Теория статистики Теория оптимизации Уроки AutoCAD 3D Уроки базы данных Access Уроки Orcad Цифровые автоматы Шпаргалки по компьютеру Шпаргалки по программированию Экспертные системы Элементы теории информации

Метасимволы


Дата добавления: 2015-06-12; просмотров: 703; Нарушение авторских прав


Мы уже встречали простые примеры использования метасимволов — вопросительных знаков в выражениях аргументов параметризации — в главах 11 и 13. В этом разделе тема будет рассмотрена более подробно. Начнем с примера, демонстрирующего одну особенность массивов: массив производного типа можно присвоить ссылке на массив базового типа:

//: generics/CovariantArrays.java

class Fruit {}

class Apple extends Fruit {}

class Jonathan extends Apple {}

class Orange extends Fruit {}

 

public class CovariantArrays {

public static void main(String[] args) {

Fruit[] fruit = new Apple[10];

fruit[0] = new Apple(); // OK

fruit[1] = new Jonathan(); // OK

// Тип времени выполнения - Apple[], а не Fruit[] или Orange[]:

try {

// Компилятор позволяет добавлять объекты Fruit:

fruit[0] = new Fruit(); // ArrayStoreException

} catch(Exception e) { System.out.println(e); }

try {

// Компилятор позволяет добавлять объекты Orange:

fruit[0] = new Orange(); // ArrayStoreException

} catch(Exception e) { System.out.println(e); }

}

}

<spoiler text="Output:">

java.1ang.ArrayStoreException: Fruit

java.1ang.ArrayStoreException: Orange

</spoiler> Первая строка main() создает массив Apple и присваивает его ссылке на массив Fruit. Выглядит логично — Apple является разновидностью Fruit, поэтому массив Apple также одновременно должен быть массивом Fruit. С другой стороны, если фактическим типом массива является Аррlе[], в массиве можно разместить только Apple или субтип Apple, причем это правило должно соблюдаться как во время компиляции, так и во время выполнения. Но обратите внимание на то, что компилятор также позволит разместить в массиве ссылку на объект Fruit. Для компилятора это вполне логично, потому что он имеет дело со ссылкой Fruit[] — так почему бы не разрешить занести в массив объект Fruit или любого типа, производного от Fruit, — скажем, Orange? Во время компиляции это разрешено. Однако механизм времени выполнения знает, что он имеет дело с Apple[], и при попытке занесения постороннего типа происходит исключение. Впрочем, для массивов это не создает особых проблем, потому что при вставке объекта неверного типа вы об этом очень быстро узнаете во время выполнения. Но одна из основных целей параметризации как раз и состоит в том, чтобы по возможности переместить выявление подобных ошибок на стадию выполнения. Итак, что же произойдет при использовании параметризованных контейнеров вместо массивов?



//: generics/NonCovariantGenerics.java

// {CompileTimeError} (Won't compile)

import java.util.*;

 

public class NonCovariantGenerics {

// Ошибка компиляции: несовместимые типы

List<Fruit> flist = new ArrayList<Apple>();

}

На первый взгляд это выглядит как утверждение «Контейнер с элементами Apple нельзя присвоить контейнеру с элементами Fruit», но следует вспомнить, что параметризация — это не только контейнеры. В действительности утверждение следует трактовать шире: «Параметризованный тип, в котором задействован тип Apple, нельзя присвоить параметризованному типу, в котором задействован тип Fruit». Если бы, как в случае с массивами, компилятор располагал достаточной информацией и мог понять, что речь идет о контейнерах, он мог бы проявить некоторую снисходительность. Но компилятор такой информацией не располагает, поэтому он отказывается выполнить «восходящее преобразование». Впрочем, это и не является восходящим преобразованием — List с элементами Apple не является «частным случаем» List с элементами Fruit. Первый может хранить Apple и подтипы Apple, а второй — любые разновидности Fruit... да, в том числе и Apple, но от этого он не становится List с элементами Apple, а по-прежнему остается List с элементами Fruit. Проблема в том, что речь идет о типе контейнера, а не о типе элементов, которые в этом контейнере хранятся. В отличие от массивов, параметризованные типы не обладают встроенной ковариантностью. Это связано с тем, что массивы полностью определяются в языке и для них могут быть реализованы встроенные проверки как во время компиляции, так и во время выполнения, но с параметризованными типами компилятор и система времени выполнения не знают, что вы собираетесь делать с типами и какие правила при этом должны действовать. Но иногда между двумя разновидностями параметризованных типов все же требуется установить некоторую связь, аналогичную восходящему преобразованию. Именно это и позволяют сделать метасимволы.

//: generics/GenericsAndCovariance.java

import java.util.*;

public class GenericsAndCovariance {

public static void main(String[] args) {

// Метасимволы обеспечивают ковариантность:

List<? extends Fruit> flist = new ArrayList<Apple>();

// Ошибка компиляции: добавление объекта

// произвольного типа невозможно

// flist.add(new Apple());

// flist.add(new Fruit());

// flist.add(new Object());

flist.add(null); // Можно, но неинтересно

// Мы знаем, что возвращается по крайней мере Fruit:

Fruit f = flist.get(0);

}

}

Теперь flist относится к типу List<? extends Fruit>, что можно прочитать как «список с элементами любого типа, производного от Fruit». Однако в действительности это не означает, что List будет содержать именно типы из семейства Fruit. Метасимвол обозначает «некоторый конкретный тип, не указанный в ссылке flist». Таким образом, присваиваемый List должен содержать некий конкретный тип (например, Fruit или Apple), но для восходящего преобразования к flist этот тип несущественен. Если единственное ограничение состоит в том, что List содержит Fruit или один из его подтипов, но вас не интересует, какой именно, что же с ним можно сделать? Если вы не знаете, какие типы хранятся в List, возможно ли безопасное добавление объекта? Нет, как и в случае с CovariantArrays.java, но на этот раз ошибка выявляется компилятором, а не системой времени выполнения. Может показаться, что такой подход не совсем логичен — вам не удастся даже добавить Apple в List, в котором, как вы только что указали, должны храниться Apple. Да, конечно, но компилятор-то этого не знает! List<? extends Fruit> вполне может указывать на List<Orange>. С другой стороны, вызов метода, возвращающего Fruit, безопасен; мы знаем, что все элементы List должны по меньшей мере относиться к Fruit, поэтому компилятор это позволит.



<== предыдущая лекция | следующая лекция ==>
Ограничения | Насколько умен компилятор?


Карта сайта Карта сайта укр


Уроки php mysql Программирование

Онлайн система счисления Калькулятор онлайн обычный Инженерный калькулятор онлайн Замена русских букв на английские для вебмастеров Замена русских букв на английские

Аппаратное и программное обеспечение Графика и компьютерная сфера Интегрированная геоинформационная система Интернет Компьютер Комплектующие компьютера Лекции Методы и средства измерений неэлектрических величин Обслуживание компьютерных и периферийных устройств Операционные системы Параллельное программирование Проектирование электронных средств Периферийные устройства Полезные ресурсы для программистов Программы для программистов Статьи для программистов Cтруктура и организация данных


 


Не нашли то, что искали? Google вам в помощь!

 
 

© life-prog.ru При использовании материалов прямая ссылка на сайт обязательна.

Генерация страницы за: 0.007 сек.