Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Тема 5.1 Общие сведения об универсальных типах



 

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

Главное преимущество универсального кода состоит в том, что он автоматически настраивается на работу с нужным типом данных. Многие алгоритмы выполняются одинаково, независимо от того, к информации какого типа данных они должны быть применены. Например, быстрая сортировка не зависит от типа данных, в качестве которого можно использовать Integer, String, Object и даже Thread. Используя универсальные типы, можно единожды реализовать алгоритм, а затем без труда применять его к любому типу данных.

Как вы уже знаете, язык Java всегда давал возможность создавать классы, интерфейсы и методы, пригодные для обработки любых типов данных. Это достигалось за счет использования класса Object. Поскольку Object является суперклассом для всех остальных классов, ссылка на Object может выступать в качестве ссылки на любой другой объект. Таким образом, до появления универсального кода для выполнения действий с любыми типами данных в классах, объектах и методах использовалась ссылка типа Object. При этом возникала серьезная проблема, связанная с необходимостью явно преобразовывать Object в другой тип. Подобное преобразование часто становилось источником ошибок. Универсальные типы повышают уровень безопасности при работе с данными, поскольку при этом все преобразования типов происходят неявно. Таким образом, универсальные типы повышают пригодность кода к повторному использованию.

Синтаксис объявления универсального класса выглядит так:

class имя_класса< параметры_типа> { //...

Синтаксис объявления ссылки на универсальный класс приведен ниже.

имя_класса< передаваемые_тип имя_переменной =

new имя_класса< передаваемые_типы> (передаваемые_значения);

В листинге 5.1 приведен простой пример использования универсальных типов.

Листинг 5.1

// Здесь Т - это параметр, который заменяется реальным именем типа

// при создании объекта Gen

public class Gen< T> {

private T ob; // Объявление объекта типа Т

// Конструктору передается ссылка на объект типа T

public Gen(T o) {

ob = o;

}

// Метод возвращает объект ob

public T getob() {

return ob;

}

// ОтображениетипаТ

public void showType() {

System.out.println(" Type of T is " + ob.getClass().getName());

}

}

// Демонстрация универсального класса

public class DemoGeneric {

public static void main(String args[]) {

// Создание ссылки на объект типа Gen< Integer>

Gen< Integer> iOb;

// Создание объекта Gen< Integer> и присваивание ссылки

// на него переменной iOb.

// Обратите внимание на то, что при помещении* значения 8

// в состав объекта Integer используется автоупаковка

iOb = new Gen< Integer> (88);

// Отображение типа данных, используемого iOb

iOb.showType();

// Получение значения iOb. Обратите внимание на то,

// что приведение типов не требуется

int v = iOb.getob();

System.out.println(" value: " + v);

System.out.println();

// Создание ссылки на объект типа Gen< String>

Gen< String> strOb = new Gen< String> (" Generics Test" );

// Отображение типа данных, используемого strOb

strOb.showType();

// Получение значения strOb. В данном случае приведение

// типов также не требуется

String str = strOb.getob();

System.out.println(" value: " + str);

}

}

Рассмотрим код программы более подробно. В первую очередь обратите внимание на способ объявления Gen. Для этого используется следующая строка кода:

public classGen< T> {

где Т – это имя параметра типа, т.е. параметра, с помощью которого задается тип данных. Данное имя впоследствии будет заменено реальным именем типа, которое передается Gen при создании объекта. Таким образом, T используется вобъектеGen там, где необходим параметр типа. Объявляемый параметр типа указывается в угловых сколках. Поскольку в Gen используется параметр типа, Gen является универсальным классом.

Обратите внимание на имя T в объявлении класса Gen. Для этой цели может быть использован любой допустимый идентификатор, но традиционно программисты применяют имя Т. Кроме того, настоятельно рекомендуется, чтобы параметр типа состоял из одной прописной буквы. Если при объявлении класса надо задать несколько параметров типа, то кроме T часто используют имена V и E.

В следующем выражении T используется для объявления объекта ob:

private Т ob; // Объявление объекта типа T

Как было сказано ранее, идентификатор T предназначен для того, чтобы быть замещенным реальным типом при создании объекта Gen. Таким образом, в качествe типа объекта ob будет принят тип, переданный посредством Т. Например, при создании объекта будет указан тип String, то объект ob будет также Принадлежать типу String.

Рассмотрим конструктор Gen.

publicGen (Tо) {

ob = o

}

Обратите внимание на то, что параметр о данного конструктора принадлежит Т. Это означает, что реальный тип о определяется типом, переданным посредством Т при создании объекта Gen. Поскольку параметр о и переменная ob принадлежат типу T, то после замены T они также будут принадлежать одному и тому же реальному типу.

Параметр типа T можно также использовать для того, чтобы задать тип значения, возвращаемого методом. Примером может служить метод getob(), код которого показан ниже.

publicТ getob() {

returnob;

}

Поскольку типом переменной ob является T, она совместима с типом, возвращаемым методом getob().

Метод showType() отображает тип Т. Эта задача решается путем вызова метода getName() объекта Class, полученного в результате вызова метода getClass() объекта ob. До сих пор мы не использовали данные средства, поэтому рассмотрим их подробнее. В классе Object определен метод getClass(). Таким образом, данный метод является членом каждого класса. Он возвращает объект Class, соответствующий типу текущего объекта. Класс Class принадлежит пакету java.lang и инкапсулирует информациюо классе. В нем определено несколько методов, которые позволяют в процессе выполнения программы получать сведения о классе. К их числу принадлежит метод getName(), возвращающий строковое представление имени класса.

Класс GenDemo демонстрирует работу универсального класса Gen. В первую очередь в нем создается вариант объекта Gen для целых чисел.

Gen< Integer> iOb;

Приведенная выше строка кода заслуживает пристального внимания. Во-первых, заметьте, что тип Integer указывается после имени Gen в угловых скобках. В данном случае Integer– это передаваемый тип, который заменяет в классе Gen параметр типа Т. Таким образом создается вариант Gen, в котором ссылки на T преобразуются в ссылки на Integer. В объекте, созданном при выполнении приведенного выражения, переменная ob и возвращаемое значение метода getob() будут принадлежать типу Integer.

Перед тем как продолжить рассмотрение универсальных типов, необходимо принять во внимание то, что компилятор Java реально не создает различные версии Gen или другого универсального класса. В принципе удобно было бы считать, что это происходит, но на самом деле все обстоит по-другому. Вместо создан разных версий компилятор удаляет всю информацию об универсальном тип и использует вместо нее приведение типа. В результате полученный объект ведет себя так, как будто в программе была создана конкретная версия класса Gen. Таким образом, в программе реально существует лишь одна версия Gen. Процесс удаления информации об универсальном типе называется стиранием (erasure). Этот механизм мы рассмотрим более подробно в конце данного модуля.

В следующей строке кода переменной iOb присваивается ссылка на экземпляра Integer-варианта класса Gen:

iOb = newGen< Integer> (88);

Обратите внимание на то, что при вызове конструктора Gen задается передаваемый тип Integer. Это необходимо, поскольку тип объекта, на который указывает ссылка (в данном случае это iOb), должен соответствовать Gen< Integer>. Если тип ссылки, возвращаемой оператором new, будет отличаться от Gen< Integer>, возникнет ошибка компиляции. Например, сообщение о такой ошибке будет сгенерировано при попытке скомпилировать выражение

iOb = new Gen< Double> (88.0); // Ошибка!

Поскольку переменная iOb принадлежит типу Gen< Integer>, ее нельзя использовать для хранения ссылки, например на объект, Gen< Double>. Возможность проверки на соответствие типов – одно из основных преимуществ универсальных типов.

Продолжим рассмотрение выражения; для удобства повторим его еще раз.

iOb = newGen< Integer> (88);

При его выполнении осуществляется автоупаковка целочисленного значения 88 в объект Integer. Дело в том, что конструктору Gen< Integer> должен передаваться параметр типа Integer. При необходимости преобразование типов может быть выполнено явно, как в следующем примере:

iOb = new Gen< Integer> (new Integer(88));

Однако в данном случае длинная строка кода не дает никаких преимуществ по сравнению с предыдущим, более компактным выражением.

Далее в программе отображается тип переменной ob, принадлежащей объекту iOb (в данном случае это тип Integer). Для получения значения ob используется следующее выражение:

intv = iOb.getob();

Поскольку метод getob() возвращает значение типа T, которое заменяется Integer, возвращаемым типом getob() будет Integer. Это значение перед присвоением переменной v (типа int) будет подвергнуто автораспаковке.

И наконец, в классе GenDemo объявляется объект типа Gen< String>.

Gen< String> strOb = new Gen< String> (" Generics Test" );

Поскольку параметр типа – это String, данный тип заменяет T в составе Gen. Таким способом создается String-вариант Gen, работу которого демонстрируют оставшиеся строки кода.

При определении экземпляра универсального класса передаваемый тип, который заменяет параметр типа, должен быть именем класса. Для этой цели нельзя использовать простой тип, например int или char. Например, в случае класса Gen можно передать в качестве T любой класс, но не простой тип. Другими словами, следующее выражение недопустимо:

Gen< int> strOb = newGen< int> (53); // Ошибка.

// Простой тип в данном случае использовать нельзя.

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

Чтобы лучше понять универсальные типы, необходимо заметить, что ссылка на конкретный вариант одного универсального типа несовместима с объектом другого универсального типа. Например, если бы в рассмотренной ранее программе присутствовала Приведенная ниже строка кода, компилятор сгенерировал бы сообщение об ошибке и не сформировал файл класса.

iOb = strOb; // Ошибка!

Несмотря на то что и iOb и strOb принадлежат типу Gen< T>, они являются ссылками на различные типы. Такое несоответствие возникает вследствие различия передаваемых типов. Эта особенность универсальных типов предотвращает ошибки при написании программ.

 


Поделиться:



Популярное:

Последнее изменение этой страницы: 2016-05-30; Просмотров: 655; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.025 с.)
Главная | Случайная страница | Обратная связь