Навигация
Главная
Поиск
Форум
FAQ's
Ссылки
Карта сайта
Чат программистов

Статьи
-Delphi
-C/C++
-Turbo Pascal
-Assembler
-Java/JS
-PHP
-Perl
-DHTML
-Prolog
-GPSS
-Сайтостроительство
-CMS: PHP Fusion
-Инвестирование

Файлы
-Для программистов
-Компонеты для Delphi
-Исходники на Delphi
-Исходники на C/C++
-Книги по Delphi
-Книги по С/С++
-Книги по JAVA/JS
-Книги по Basic/VB/.NET
-Книги по PHP/MySQL
-Книги по Assembler
-PHP Fusion MOD'ы
-by Kest
Professional Download System
Реклама
Услуги

Автоматическое добавление статей на сайты на Wordpress, Joomla, DLE
Заказать продвижение сайта
Программа для рисования блок-схем
Инженерный калькулятор онлайн
Таблица сложения онлайн
Популярные статьи
OpenGL и Delphi... 65535
Форум на вашем ... 65535
HACK F.A.Q 65535
Бип из системно... 65535
Гостевая книга ... 65535
Содержание сайт... 65535
Вызов хранимых ... 65535
Эмулятор микроп... 65535
Подключение Mic... 65535
Приложение «Про... 65535
Оператор выбора... 65535
Программируемая... 64665
Invision Power ... 64072
Организация зап... 63493
Модуль Forms 60360
Создание отчето... 60342
ТЕХНОЛОГИИ ДОСТ... 56634
Пример работы с... 53748
Создание потоко... 52239
Имитационное мо... 52062
Реклама
Сейчас на сайте
Гостей: 5
На сайте нет зарегистрированных пользователей

Пользователей: 13,061
новичок: FooBar
Новости
Реклама
Выполняем курсовые и лабораторные по разным языкам программирования
Подробнее - курсовые и лабораторные на заказ
Delphi, Turbo Pascal, Assembler, C, C++, C#, Visual Basic, Java, GPSS, Prolog, 3D MAX, Компас 3D
Заказать программу для Windows Mobile, Symbian

Компьютерный магазин на Turbo Pascal (База данных) + Пояснительная записка
Моделирование интернет кафе на GPSS + Отчет
Расчет обратной матрицы на Delphi + Пояснительная записка

Реклама



Подписывайся на YouTube канал о программировании, что бы не пропустить новые видео!

ПОДПИСЫВАЙСЯ на канал о программировании
Настраиваемые типы
Среди множества расширений языка, включенных в Java 2 версии 5.0, средства настройки типов (generics) оказали на язык наиболее глубокое влияние. Они не только добавили новый синтаксический элемент в язык Java, но и вызвали изменения во многих классах и методах API (Application Programming Interface, Интерфейс прикладного программирования) ядра. Благодаря применению настраиваемых типов стало возможным создавать классы, интерфейсы и методы, работающие с различными типами данных, при этом обеспечивая безопасность типов. Многие алгоритмы логически идентичны вне зависимости от используемых типов данных. Например, механизм поддержки стека одинаков для стеков, хранящих элементы типа Integer, String, Object или Thread. С помощью настраиваемых типов Вы можете определить один алгоритм независимо от конкретного типа данных и затем применять его без дополнительной доработки к различным типам данных. Функциональные возможности настраиваемых типов коренным образом меняют подход к написанию программ на языке Java.
Пожалуй, включение в язык настраиваемых типов оказало наибольшее влияние на средства работы с группами объектов (Collections Framework). Как известно, в подсистеме Collections Framework определено несколько классов, таких как списки (list) и отображения (mар), которые управляют обработкой, коллекций. Классы, описывающие коллекции, могут использоваться для любого типа объекта. Введение настраиваемых типов обеспечивает этим классам полную типовую безопасность (type safety). Таким образом, кроме включения в язык новой мощной функциональной возможности, применение настраиваемых типов позволяет существенно улучшить использование средств, уже существующих в языке. Именно поэтому настраиваемые типы представляют собой столь важное расширение языка Java.
Что такое настраиваемые типы
По существу настраиваемые типы — это параметризованные типы (parameterized type). Они очень важны, так как позволяют Вам создавать классы,интерфейсы и методы, в которых тип обрабатываемых данных задается как параметр. С помощью настраиваемых типов можно, например, создать один класс, автоматически оперирующий разными типами данных. Класс, интерфейс или метод, работающие с параметризованными типами, называются настраиваемыми (generic).
Важно помнить, что в языке Java, всегда существовала возможность создавать обобщенные или универсальные (generalized) классы, интерфейсы и методы с помощью ссылки на тип Object. Поскольку тип Object — это суперкласс для всех остальных классов, ссылка на него, по сути, позволяет ссылаться на объект любого типа. Таким образом, в исходных текстах программ, существовавших до появления настраиваемых типов, обобщенные классы, интерфейсы и методы использовали ссылки на тип Object для работы с различными типами данных. Проблема заключалась в том, что подобная обработка не обеспечивала типовой безопасности.
Средства настройки типов обеспечили недостающую типовую безопасность. Они также модернизировали процесс, потому что отпала необходимость в явном приведении типов (Type cast) при переходе от типа Object к конкретному обрабатываемому типу. Благодаря введению настраиваемых типов все приведения выполняются автоматически и скрыто. Таким образом, применение настраиваемых типов расширяет возможности повторного использования кода и делает этот процесс легким и безопасным.
Предупреждение
Предупреждение программистам на языке C++: несмотря на то, что настраиваемые типы подобны шаблонам в C++, это не одно и то же. Существуют принципиальные различия между двумя подходами к настраиваемым типам (generic type) в этих языках. Если Вы знаете язык C++, важно не спешить с выводами по поводу принципов работы настраиваемых типов в Java.
Простой пример применения настраиваемых типов
Давайте начнем с простого примера, настраиваемого или полиморфного класса. В приведенной в листинге 3.1 программе определены два класса. Первый — настраиваемый класс Gen, второй — GenDemo, используюший класс Gen.
Листинг 3.1. Пример простого настраиваемого класса
//T — тип, объявленный как параметр, и
// будет заменен реальным типом
// при создании объекта типа
class Gen {
T ob; // объявляет объект типа Т
// Передает конструктору ссылку на
// объект типа
Gen(T o) {
ob = o;
}
// Возвращает объект
T getob() {
return ob;
}

// Показывает тип Т.
void showType() {
System.out.println("Type of T is " + ob.getClass().getName());
}
}

// Демонстрирует настраиваемый класс.
class GenDemo {
public static void main(String args[]) {
// Создает ссылку на Gen для объектов типа Integer.
Gen iOb;
// Создает объект типа Gen и присваивает
// ссылку на него iOb. Обратите внимание на использование
// автоупаковки (autoboxing)
// для инкапсуляции значения 88 в объекте типа Integer.
iOb = new Gen(88);
// Показывает тип данных iOb.
iOb.showType();
// Получает значение, хранящееся в iOb. Обратите внимание на то,
// что не требуется никакого приведения типов.
int v = iOb.getob();
System.out.println("value: " + v);

System.out.println();
// Создает объект типа Gen для строк.
Gen strOb = new Gen("Generics Test");

// Показывает тип данных переменной strOb.
strOb.showType();

// Получает значение, хранящееся в strOb. И опять
// никакого приведения типов не требуется.
String str = strOb.getob();
System.out.println("value: " + str);
}
}



Далее приведен вывод, формируемый программой.
Type of T is java.lang.Integer
value: 88
Type of T is java.lang.String
value: Generics Test
Давайте подробно обсудим программу из листинга 3.1.
Прежде всего, обратите внимание на приведенное в следующей строке объявление типа Gen.
Class Gen {
Здесь T — тип, объявленный как параметр, или параметр типа (type parameter). Это имя используется как заменитель действительного типа, передаваемого в класс Gen при создании объекта. Таким образом, имя T при описании класса используется везде, где требуется параметр для типа. Обратите внимание на то, что имя T обрамляется символами: <>. Эту синтаксическую конструкцию можно обобщить: всегда при объявлении типа как параметра его имя заключается в угловые скобки. Поскольку класс Gen использует тип, объявленный как параметр, он является настраиваемым классом или параметризованным типом.
В приведенной далее строке тип T применяется для объявления объекта, названного ob.
Т ob; // объявляет объект типа Т
Как уже объяснялось, имя T — заменитель названия реального типа, который будет задан при создании объекта типа Gen. Таким образом, объект ob получит тип, переданный параметром T Например, если в параметре T передается тип String, у объекта ob будет тип String.
Теперь рассмотрим конструктор класса Gen.
Gen(T о) {
ob = о;
}



Обратите внимание на то, что у параметра о тип T. Это означает, что действительный тип переменной o определяется типом, передаваемым в параметрре T в момент создания объекта класса Gen. Кроме того, поскольку для параметра o и переменной-члена ob задан тип T, они получат один и тот же действительный тип в момент создания объекта класса Gen.
Тип T, объявленный как параметр, можно также использовать для задания типа данных, возвращаемых методом, как в случае метода getob(), приведенного в следующих строках.
T
getob() {
return ob;
}



Поскольку у объекта ob тип T, его тип совместим с типом данных, возвращаемых методом getob().
Метод showType() отображает на экране название типа T с помощью вызова метода getName() для объекта типа class, возвращенного методом getClass(), который вызывается для объекта ob. Метод getClass() определен в классе Object и таким образом является членом всех классов. Он возвращает объект типа Сlass, который соответствует типу класса того объекта, для которого он был вызван. В типе Сlass определен метод getName( ), возвращающий строковое представление имени класса.
Класс GenDemo демонстрирует применение класса Gen. Сначала он создает вариант этого класса для целочисленных объектов, как показано в приведенной далее строке.
Gen iOb;
Посмотрите внимательно на это объявление. Прежде всего, обратите внимание на то, что тип Integer задан в угловых скобках после названия класса Gen. В этом случае Integer — это аргумент, определяющий тип, или аргумент типа (type argument), который передается параметру T класса Gen. Это эффективный способ создания версии класса Gen, в которой все ссылки на тип T будут преобразованы в ссылки на тип Integer. Таким образом, в приведенном объявлении у объекта ob тип Integer и метод getob{) возвращает объект типа Integer.
Прежде чем продолжать, следует отметить, что компилятор языка Java на самом деле не создает разные версии класса Gen или других настраиваемых классов. Хотя это удобная абстрактная модель, она не соответствует тому, что происходит на самом деле. Вместо этого компилятор удаляет всю информацию о настраиваемом типе, применяя необходимые преобразования типов, для того чтобы заставить Вашу программу вести себя так, как будто в ней создается конкретная версия класса Gen. Таким образом, в Вашей программе действительно существует только одна версия класса Gen. Процесс удаления информации о настраиваемом типе называется стиранием или подчисткой (erasure) и мы вернемся к его обсуждению позже в этой главе.
В приведенной далее строке переменной ob присваивается ссылка на экземпляр версии Integer класса Gen.
iOb = new Gen(88) ;
Обратите внимание на то, что при вызове конструктора класса Gen также задается Integer — аргумент типа. Это действие необходимо, так как у объекта
(В данном случае iOb), которому присваивается ссылка, тип Gen.
Следовательно, ссылка, возвращаемая операцией new, также должна указывать на объект типа Gen. Если условие не соблюдается, возникнет ошибка во время компиляции, подобная приведенной далее.
iOb = new Gen(88.0); //Ошибка!
Поскольку у объекта ob тип Gen, он не может использоваться для ссылки на объект типа Gen. Такая проверка соответствия типов — одно из главных преимуществ настраиваемых типов, обеспечивающее типовую безопасность (type safety).
Как сказано в комментариях к программе из листинга 3.1, в приведенном далее присваивании:
iOb = new Gen(88);
применяется автоупаковка для инкапсуляции значения 88 базового типа int в тип Integer. Этот действие выполняется потому, что тип Gen порождает конструктор, принимающий аргумент Integer. Поскольку ожидается тип Integer, Java автоматически инкапсулирует 88 в объект этого типа. Конечно, можно описать это действие в явном виде, как показано в следующей строке:
iOb = new Gen(new Integer(88));
Однако Вы не получите никакого выигрыша от применения этого варианта.
Далее программа отображает на экране тип Integer — это тип переменной-члена ob из объекта iOb. Далее программа получает значение переменной ob с помощью следующей строки кода:
int v = iOb.getob();
У данных, возвращаемых методом getob() — тип T, который был заменен типом Integer при объявлении объекта iOb, следовательно, метод getob( ) также возвращает объект типа Integer, который распаковывается в значение типа int при присваивании переменной v (базового типа int). Таким образом, нет необходимости приводить в соответствие тип, возвращаемый методом getob(), к типу Integer. Конечно, не нужно явно использовать распаковку. Приведенную строку можно записать в следующем виде:
int v = iOb.getob( ). intValue( );
но автораспаковка делает код короче.
Далее в классе GenDemo объявляется объект типа Gen:
Gen strOb = new Gen("Generics Test");
Поскольку задан аргумент типа String, тип String замещает параметр типа T внутри класса Gen. Эта замена создает (логически) версию String класса Gen, что демонстрируют оставшиеся строки листинга 3.1.
Средства настройки типов работают только с объектами
При объявлении экземпляра настраиваемого типа аргумент типа, передаваемый типу, объявленному как параметр, должен быть каким-либо классом. Вы не можете использовать для этой цели базовый тип, такой как int или char. Например, в класс Gen можно передать любой класс как тип в параметре Т, но нельзя заменить Т ни одним базовым типом. Следовательно, приведенная далее строка кода недопустима:
Gen strOb = new Gen(53); // Ошибка, нельзя использовать
// базовый тип
Конечно, запрет на задание базового типа нельзя считать серьезным ограничением, т. к. Вы можете применять оболочки типов (как было показано в примерах главы 2) для инкапсуляции базового типа. Более того, механизм автоупаковки и автораспаковки языка Java делает применение оболочек типов очевидным.

Различия настраиваемых типов, основанных
на разных аргументах типа

Для понимания механизма настройки типов важно уяснить, что две ссылки на разные конкретные версии одного и того же настраиваемого типа не совместимы по типу. Например, если вставить приведенную далее строку кода в листинг 3.1, компиляция завершится с ошибкой:
iOb - strOb; // Неправильно!
Несмотря на то, что у переменных iOb и strOb тип Gen, они ссылаются на объекты разных типов потому, что их аргументы типа отличаются. Это подход используется для усиления типовой защиты и предупреждения ошибок.

Как настраиваемые типы улучшают типовую безопасность

С этой точки зрения следовало бы спросить себя: "Можно ли реализовывать функциональную возможность, которой обладает класс Gen, без применения настраиваемых типов, простым заданием типа данных Object и выполнением необходимых операций приведения типов? Каков выигрыш от применения настраиваемого класса Gen?" Ответ — применение настраиваемых типов автоматически обеспечивает типовую безопасность всех операций с участием настраиваемого типа Gen. При этом они избавляют Вас от необходимости вручную вводить код для контроля и приведения типов.
Для того чтобы понять выгоды применения настраиваемых типов, прежде всего рассмотрим приведенную в листинге 3.2 программу, которая создает ненастраиваемый эквивалент класса Gen.

Листинг 3.2. Создание непараметризованного типа NonGen
// NonGen функционально эквивалентен классу Gen
// но не использует средства настройки типов.
class NonGen {
Object ob;
// Передает конструктору ссылку на
// объект типа Object
NonGen(Object o) {
ob = o;
}

// Возвращает тип Object
Object getob() {
return ob;
}

// Показывает тип ob.
void showType() {
System.out.println("Type of ob is " +
ob.getClass().getName());
}
}

// Демонстрирует ненастраиваемый класс
class NonGenDemo {
public static void main(String args[]) {
NonGen iOb;
// Создает объект типа NonGen и запоминает
// целое число в нем. Выполняется автоупаковка.
iOb = new NonGen(88);

// Показывает тип объекта iOb.
iOb.showType();
// Получает значение, хранящееся в iOb.
// В этот момент требуется приведение типов.
int v = (Integer) iOb.getob();
System.out.println("value: " + v);

System.out.println()
// Создает другой объект типа NonGen и
// запоминает в нем строку.
NonGen strOb = new NonGen("Non-Generics Test");
// Показывает тип данных объекта strOb.
strOb.showType();

// Получает значение, хранящееся в strOb.
// Снова необходимо приведение типов.
String str = (String) strOb.getob();
System.out.println("value: " + str);
// Эта строка компилируется, но концептуально неправильна!
iOb = strOb;
v = (Integer)iOb.getob(); // runtime error!
}
}



В листинге 3.2 есть несколько интересных фрагментов. Прежде всего, в классе NonGen все вхождения параметра T заменены типом Object. Это замещение позволяет классу NonGen хранить объекты любого типа, как и в случае настраиваемого типа. Но при этом у компилятора нет никаких реальных сведений о том, какой конкретный тип данных содержится в NonGen. Подобная ситуация плоха по двум причинам. Во-первых, для извлечения хранящихся данных должны выполняться явные приведения типов. Во-вторых, разнообразные ошибки несовместимости типов невозможно обнаружить до запуска программы. Обсудим каждую проблему подробно.
Обратите внимание на приведенную далее строку: int v = (Integer)iOb.getob();
Поскольку метод getob( ) возвращает тип Object, понадобится преобразование в тип Integer для автораспаковки значения и сохранения его в переменной v. Если Вы удалите операцию приведения типа, программа не будет компилироваться. При использовании настраиваемого класса это действие скрыто от Вас. В случае применения не настраиваемого типа, приведение типа следует выполнять явно. Это не только неудобство, но и является потенциальным источником ошибок.
Теперь рассмотрим следующий фрагмент из заключительной части листинга 3.2.
// Эта строка компилируется, но концептуально неправильна!
Ob = StrOb;
v = (Integer)iOb.getob() ; // ошибка во время выполнения!
Здесь переменной iOb присваивается переменная strOb. Однако strOb ссылается на объект, содержащий строку, а не целое число. Синтаксически такое присваивание корректно, поскольку ссылки типа NonGen одинаковы, и любая ссылка типа NonGen может быть перенаправлена на другой объект этого типа. Тем не менее описываемый оператор семантически неверен, что подтверждается следующей строкой. В ней тип данных, возвращаемых методом getob() приводится к типу Integer и затем делается попытка присвоить это значение переменной v. Беда в том, что объект iOb уже ссылается на объект, содержащий тип String, не Integer. К сожалению, без использования настраиваемых типов невозможно известить об этом компилятор. Вместо этого возникает ошибка времени выполнения (runtime error) при попытке приведения к типу Integer. Как Вам известно, наличие в Вашей программе исключений, генерируемых во время выполнения, крайне нежелательно.
Описанная ситуация никогда не возникнет при применении настраиваемых типов. Если такой фрагмент кода встретится в версии программы, использующей настраиваемые типы, компилятор обнаружит ошибку и сообщит о ней, предупреждая тем самым серьезный сбой, возникающий при генерации исключения во время выполнения. Возможность разработки кода, обеспечивающего типовую безопасность и позволяющего обнаруживать ошибки несоответствия типов на этапе компиляции, — важнейшее преимущество использования настраиваемых типов. Несмотря на то, что с помощью ссылок типа Object всегда можно создать "настраиваемый" код, в нем не будет должной защиты типов и их неправильное использование может привести к возникновению исключительных ситуаций во время выполнения программы. Применение настраиваемых типов препятствует их возникновению. По существу, благодаря применению настраиваемых типов ошибки времени выполнения можно превратить в ошибки этапа компиляции. А это важное преимущество.
Опубликовал Kest January 13 2009 00:42:36 · 0 Комментариев · 9158 Прочтений · Для печати

• Не нашли ответ на свой вопрос? Тогда задайте вопрос в комментариях или на форуме! •


Комментарии
Нет комментариев.
Добавить комментарий
Имя:



smiley smiley smiley smiley smiley smiley smiley smiley smiley
Запретить смайлики в комментариях

Введите проверочный код:* =
Рейтинги
Рейтинг доступен только для пользователей.

Пожалуйста, залогиньтесь или зарегистрируйтесь для голосования.

Нет данных для оценки.
Гость
Имя

Пароль



Вы не зарегистрированны?
Нажмите здесь для регистрации.

Забыли пароль?
Запросите новый здесь.
Поделиться ссылкой
Фолловь меня в Твиттере! • Смотрите канал о путешествияхКак приготовить мидии в тайланде?
Загрузки
Новые загрузки
iChat v.7.0 Final...
iComm v.6.1 - выв...
Visual Studio 200...
CodeGear RAD Stud...
Шаблон для новост...

Случайные загрузки
Report
Пример работы с б...
iComm v.6.1 - выв...
Сложный калькулятор
EditNew
RxLIB
Delphi 7 Enterpri...
C# Учебный курс
AUTOWEB
Исправление проц...
Разработка интерн...
RSS Feeds
Редактор анимаций
JBlabel3D
XPButtons
Counter [Исходник...
Алгоритм трассиро...
Открытие Cd-ROM'a...
Assistant
Библия для програ...

Топ загрузок
Приложение Клие... 100371
Delphi 7 Enterp... 82744
Converter AMR<-... 20046
Borland C++Buil... 11139
GPSS World Stud... 10695
Borland Delphi ... 8082
Turbo Pascal fo... 6968
Visual Studio 2... 4963
Калькулятор [Ис... 4313
FreeSMS v1.3.1 3510
Случайные статьи
От людей нужно ожи...
Режимы экспозиции ...
Как улучшить качес...
Как попробовать се...
ЭТАП 2. СМЫСЛОВОЕ ...
• Специальные функ...
Последниедва запре...
Настройки чтения
Форматирование выв...
Механизм сигналов ...
Доставка грузов
Пишем троян
установки)300 клие...
Так, пользователь ...
Глава 3. Классы
Список ВСЕХ русски...
16-Ю)
Общие характеристи...
Реальный размер жу...
RELEASE (ОСВОБОДИТЬ)
2.5. РЕШЕНИЕ: СОЗД...
Canon: выбор атмос...
Защищайте доступно...
Процедура RestoreC...
Invalid function n...
Статистика



Друзья сайта
Программы, игры


Полезно
В какую объединенную сеть входит классовая сеть? Суммирование маршрутов Занимают ли таблицы память маршрутизатора?