Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Герберт Шилдт. Java 2, v5.0 (Tiger). Новые возм...doc
Скачиваний:
5
Добавлен:
01.03.2025
Размер:
1.21 Mб
Скачать

Настраиваемый класс с двумя параметрами типа

Вы можете объявить несколько параметров типа в настраиваемом типе данных. Для определения двух или нескольких параметров типа используйте список, разделенный запятыми. Например, приведенный в листинге 3.3 класс TwoGen представляет собой вариант класса Gen с двумя параметрами типа.

Листинг 3.3. Пример простого настраиваемого класса с двумя параметрами

class TwoGen<T, V> {

T ob1;

V ob2;

// Передает конструктору ссыпку на

// объекты типов Т и V.

TwoGen(T o1, V o2) {

ob1 = o1;

ob2 = o2;

}

// Показывает типы Т и V.

void showTypes() {

System.out.println("Type of T is " +

ob1.getClass().getName());

System.out.println("Type of V is " +

ob2.getClass().getName());

}

T getob1() {

return ob1;

}

V getob2() {

return ob2;

}

}

// Демонстрирует использование класса TwoGen.

class SimpGen {

public static void main(String args[]) {

TwoGen<Integer, String> tgObj =

new TwoGen<Integer, String>(88, "Generics");

// Show the types.

tgObj.showTypes();

// Obtain and show values.

int v = tgObj.getob1();

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

String str = tgObj.getob2();

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

}

}

Далее приведен вывод результата работы программы из листинга 3.3.

Type of T is Java.lang.Integer

Type of V is Java.lang.String

value: 88

value: Generics

Обратите внимание на объявление класса TwoGen:

class TwoGen<T, v> {

В нем задаются два параметра: T и v, разделенные запятыми. Поскольку у класса два параметра типа, при создании объекта следует передать в типе TwoGen два аргумента типа, как показано в следующих строках: TwoGen<Integer, String> tgObj =

new TwoGen<:Integer, String>(88, "Generics");

В данном случае тип T заменяется типом Integer, а тип v — типом String. Несмотря на то, что в приведенном примере аргументы типа разные, можно применять и одинаковые аргументы. Приведенный в следующей строке код полностью корректен:

TwoGen< String, String> x = new TwoGen< String, String>("A", "B");

В этом случае и T, и V заменяются типом String. Правда, если аргументы

типа всегда одинаковы, нет необходимости использовать два параметра типа.

Общий вид объявления настраиваемого класса

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

class имя-класса<список-парамегров-типа> {//...

Далее приведена синтаксическая запись для объявления ссылки на настраиваемый класс.

class имя-класса<список-аргумеитов-типа> имя-переменной =

new имя-класса<список-аргументов-типа>(список-констант-аргументов);

Ограниченные типы

В предыдущих примерах параметры типа могли заменяться классами любого типа. В большинстве случаев это замечательно, но иногда полезно ограничить количество типов, которые можно передавать параметру типа. Например, Вы хотите создать настраиваемый класс, который содержит метод, возвращающий среднее арифметическое элементов числового массива. Более того, хотите использовать этот класс для получения среднего арифметического элементов массивов разных числовых типов, включая целые, числа с плавающей запятой и двойной точности. Следовательно, следует задать тип элементов массива в общем виде с помощью параметра типа. Для создания такого класса можно попытаться написать код, похожий на приведенный в листинге 3.4.

Листинг 3.4. Неудачная попытка создать настраиваемый класс дли вычисления среднего арифметического элементов массива любого заданного числового типа

// Класс содержит ошибку!

class Stats<T> {

T[] nums;

// передает конструктору ссылку на

// массив типа Т.

Stats(T[] o) {

nums = o;

}

// Возвращает тип double во всех cлучаях,

double average() {

double sum = 0.0;

for(int i=0; i < nums.length; i++)

sum += nums[i].doubleValue(); // Error!!!

return sum / nums.length;

}

}

В классе Stats метод average() пытается получить все элементы массива num, приведенные к типу double с помощью метода doubleValue (). Поскольку все числовые классы, такие как Integer и Double, являются подклассами класса Number, а в классе Number определен метод doubleValue(), этот метод доступен для всех числовых классов-оболочек. Но проблема состоит в том, что компилятор ничего не знает о Вашем намерении создавать объекты типа Stats, используя только числовые типы для замены параметра типа т. Более того, Вам нужно каким-либо способом обеспечить действительную передачу только числовых типов. Для обработки подобных ситуаций язык Java предлагает ограниченные типы (bounded types). При объявлении параметра типа Вы можете задать верхнюю границу, определяющую суперкласс, от которого должны быть унаследованы все аргументы типа. Такое ограничение устанавливается с помощью ключевого слова extends при описании параметра типа, как показано в следующей строке:

<Т extends superclass>

Приведенное объявление указывает на то, что параметр T можно заменить только типом superclass или его подклассами (производными от него классами). Таким образом, superclass задает верхнюю границу включительно.

Вы можете использовать суперкласс Number как верхнюю границу для настройки класса Stats, описанного ранее (листинг 3.5).

Листинг 3.5. Использование ограниченного типа при объявлении класса Stats

// В этой версии класса Stats, аргумент типа для

// Т должен быть Number, или производный

// от Number класс.

class Stats<T extends Number> {

T[] nums; // массив типа Number или его подкласса

// Передает конструктору ссылку на

// массив типа Number или его подкласса.

Stats(T[] o) {

nums = o;

}

// Возвращает тип double в любом случае.

double average() {

double sum = 0.0;

for(int i=0; i < nums.length; i++)

sum += nums[i].doubleValue();

return sum / nums.length;

}

}

// Демонстрирует применение класса Stats.

class BoundsDemo {

public static void main(String args[]) {

Integer inums[] = { 1, 2, 3, 4, 5 };

Stats<Integer> iob = new Stats<Integer>(inums);

double v = iob.average();

System.out.println("iob average is " + v);

Double dnums[] = { 1.1, 2.2, 3.3, 4.4, 5.5 };

Stats<Double> dob = new Stats<Double>(dnums);

double w = dob.average();

System.out.println("dob average is " + w);

// Эти строки не будут компилироваться,так как String

// не является подклассом суперкласса Number.

// String strs[] = { "1", "2", "3", "4", "5" };

// Stats<String> strob = new Stats<String>(strs);

// double x = strob.average() ;

// System.out.printlnf"strob average is " + v);

}

}

Далее приведен вывод результатов работы программы из листинга 3.5.

Average is 3.0

Average is 3.3

Обратите внимание на новое объявление класса Stats, приведенное в следующей строке: class Stats<T extends Number> {

Поскольку теперь тип т ограничен суперклассом Number, компилятор языка Java знает, что все объекты типа т могут вызывать метод doubleValue(), определенный в суперклассе Number. Это само по себе значительное преимущество. Но кроме этого, ограничение типа т препятствует созданию нечисловых объектов типа Stats. Если удалить символы комментария из заключительных строк листинга 3.5, а затем выполнить компиляцию, Вы получите ошибки на этапе компиляции, так как тип String не является подклассом суперкласса Number.