Чтение онлайн

на главную - закладки

Жанры

Java: руководство для начинающих
Шрифт:

} Выполнение этой программы дает следующий результат:

Testing iOb and dOb. Absolute values are equal.

Testing iOb and 10b. Absolute values differ. Обратите внимание на два следующих вызова метода absEqual :

if(iOb.absEqual(dOb))

if(iOb.absEqual(10b)) В первом вызове переменная iOb указывает на объект типа NumericFns<Integer>, а переменная dOb — на объект типа NumericFns<Double>. Благодаря применению ме- тасимвольного аргумента по ссылке на объект iOb удается передать объект dOb методу absEqual . Подобным образом формируется и другой вызов, в котором методу передается объект типа NumericFns<Long>. И последнее замечание: не следует забывать, что метасимвольные аргументы не оказывают влияния на тип создаваемого объекта в классе NumericFns. Для этой цели служит оператор extends, указываемый в объявлении класса NumericFns. Метасимвольный аргумент лишь указывает на соответствие любому допустимому объекту класса NumericFns. ## Ограниченные

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

class А { // ... }

class В extends А { // ... }

class С extends А { // ... }

// Обратите внимание на то, что D не является подклассом А. class D { // ... } Здесь класс А является суперклассом для классов В и С, но не для класса D. Теперь рассмотрим очень простой обобщенный класс.

// Простой обобщенный класс. class Gen { ^ Т ob; Gen(Т о) { ob = о; }

} В классе Gen предусмотрен один параметр типа, который определяет тип объекта, хранящегося в переменной ob. Как видите, на тип Т не накладывается никаких ограничения. Следовательно, параметр типа Т может обозначать любой класс. А теперь допустим, что требуется создать метод, принимающий аргумент любого типа, соответствующего объекту класса Gen, при условии, что в качестве параметра типа этого объекта указывается класс А или его подклассы. Иными словами, требуется создать метод, который оперирует только объектами типа Gen<тип>, где тип — это класс А или его подклассы. Для этой цели нужно воспользоваться ограниченным метасимволь- ным аргументом. Ниже для примера приведено объявление метода test , которому в качестве аргумента может быть передан только объект класса Gen, на параметр типа которого накладываются следующие ограничения: соответствие классу А или его подклассам.

// Здесь знак ? устанавливает соответствие // классу А или производным от него подклассам, static void test(Gen<? extends A> o) { // ... } А приведенный ниже пример класса демонстрирует типы объектов класса Gen, которые могут быть переданы методу test .

class UseBoundedWildcard { // Здесь знак ? устанавливает соответствие // классу А или производным от него подклассам. //В объявлении этого метода используется ограниченный // метасимвольный аргумент. static void test(Gen<? extends A> о) { // ... } public static void main(String args[]) { A a = new A; В b = new В ; С с = new C; D d = new D ; Gen<A> w = new Gen<A>(a); Gen<B> w2 = new Gen<B>(b); Gen<C> w3 = new Gen<C>(c); Gen<D> w4 = new Gen<D>(d); // Эти вызовы метода test допустимы, так как // объекты w, w2 и w3 относятся к подклассам А. test(w); test(w2); test(w3); //А этот вызов метода test недопустим, так как // объект не относится к подклассу Л. // test(w4); // Ошибка! }

} В методе main создаются объекты классов А, В, С и D. Затем они используются для создания четырех объектов класса Gen (по одному на каждый тип). После этого метод test вызывается четыре раза, причем последний его вызов закомментирован. Первые три вызова вполне допустимы, поскольку w, w2 и w3 являются объектами класса Gen, типы которых определяются^ классом А или производными от него классами. А последний вызов метода test недопустим, потому что w4 — это объект класса D, не являющегося производным от к класса А. Следовательно, ограниченный метасимвольный аргумент в методе test не позволяет передавать ему объект w4 в качестве параметра. В целом верхняя граница для метасимвольного аргумента задается в следующей общей форме:

<? extends суперкласс > где после ключевого слова extends указывается суперкласс, т.е. имя класса, определяющего верхнюю границу, включая и его самого. Это означает, что в качестве аргумента допускается указывать не только подклассы данного класса, но и сам этот класс. По мере необходимости можно также указать нижнюю границу для метасимвольного аргумента. Для этой цели служит ключевое слово super, указываемое в следующей общей форме:

<? extends подкласс > В данном случае в качестве аргумента допускается использовать только суперклассы, от которых наследует подкласс, исключая его самого. Это означает, что подкласс, определяющий нижнюю границу, не относится к числу классов, передаваемых в качестве аргумента. В этом случае следующее приведение типов может быть выполнено, поскольку переменная х указывает на экземпляр класса Gen<Integer>:

(Gen) х // Допустимо А следующее приведение типов не может быть выполнено, поскольку переменная х не указывает на экземпляр класса Gen<Long>:

(Gen) х // Недопустимо ## Обобщенные методы Как было показано в предыдущих примерах, методы в обобщенных классах могут быть объявлены с параметром типа своего класса, а следовательно, такие методы автоматически

становятся обобщенными относительно параметра типа. Но можно также объявить обобщенный метод с одним или несколькими параметрами его собственного типа. Более того, такой метод может быть объявлен в обычном, а не обобщенном классе. Ниже приведен пример программы, в которой объявляется класс GenericMethodDemo, не являющийся обобщенным. В этом классе объявляется статический обобщенный метод arraysEqualO, в котором определяется, содержатся ли в двух массивах одинаковые элементы, расположенные в том ж самом порядке. Такой метод можно использовать для сравнения двух массивов одинаковых или совместимых между собой типов.

// Пример простого обобщенного метода, class GenericMethodDemo { // Этот обобщенный метод определяет, // совпадает ли содержимое двух массивов. static <Т, V extends Т> boolean arraysEqual(Т[] х, V[] у) { // Если массивы имеют разную длину, они не могут быть одинаковыми, if(х.length != у.length) return false; for(int i=0; i < x.length; i++) if(!x[i].equals(y[i])) return false; // Массивы отличаются. return true; // Содержимое массивов совпадает. } public static void main(String args[]) { Integer nums[] = { 1, 2, 3, 4, 5 }; Integer nums2[] = {1, 2, 3, 4, 5 }; Integer nums3[] = {1, 2, 7, 4, 5 }; Integer nums4[] = {1, 2, 7, 4, 5, 6}; // Аргументы типа T и V неявно определяются при вызове метода. if(arraysEqual(nums, nums)) System.out.println("nums equals nums"); if(arraysEqual(nums, nums2)) System.out.println("nums equals nums2"); if(arraysEqual(nums, nums3)) System.out.println("nums equals nums3"); if(arraysEqual(nums, nums4)) System.out.println("nums equals nums4"); // создать массив объектов типа Double Double dvals[] = { 1.1, 2.2, 3.3, 4.4, 5.5 }; // Следующая строка не будет скомпилирована, так как // типы массивов nums и dvals не совпадают. // if(arraysEqual(nums, dvals)) // System.out.println("nums equals dvals"); }

} Результат выполнения данной программы выглядит следующим образом:

nums equals nums nums equals nums2 Рассмотрим подробнее исходный код метода arraysEqual . Посмотрите прежде всего, как он объявляется:

static <Т, V extends Т> boolean arraysEqual(Т[] х, V[] у) { Параметры типа указываются перед возвращаемым типом. Обратите далее внимание на то, что верхней границей для типа параметра V является тип параметра Т. Таким образом, тип параметра V должен быть таким же, как и у параметра Т, или же быть его подклассом. Такая связь гарантирует, что при вызове метода arraysEqual могут быть указаны только совместимые друг с другом параметры. И наконец, обратите внимание на то обстоятельство, что метод arraysEqual объявлен как static, т.е. его можно вызывать независимо от любого объекта. Но обобщенные методы не обязательно должны быть статическими. В этом смысле на них не накладывается никаких ограничений. А теперь проанализируем, каким образом метод arraysEqual вызывается в методе main . Для этого используется обычный синтаксис, а параметры типа не указываются. И это становится возможным потому, что типы аргументов данного метода распознаются автоматически, а типы параметров Т и V настраиваются соответствующим образом. Рассмотрим в качестве примера первый вызов метода arraysEqual :

if(arraysEqual(nums, nums)) В данном случае типом первого аргумента является Integer, который и заменяет тип параметра Т. Таким же является и тип второго аргумента, а следовательно, тип параметра V также заменяется на Integer. Следовательно, выражение для вызова метода arraysEqual составлено правильно, и оба массива можно сравнить друг с другом. Обратите далее внимание на следующие закомментированные строки:

// if(arraysEqual(nums, dvals)) // System.out.println("nums equals dvals"); Если удалить в них символы комментариев и попытаться скомпилировать программу, то компилятор выдаст сообщение об ошибке. Дело в том, что верхней границей для типа параметра V является тип параметра Т. Этот тип указывается после ключевого ело- ва extends, т.е. тип параметра V может быть таким же, как и у параметра т, или быть его подклассом. В данном случае типом первого аргумента рассматриваемого здесь метода является Integer, заменяющий тип параметра т, тогда как типом второго аргумента — Double, не являющийся подклассом Integer. Таким образом, вызов метода arraysEqual оказывается недопустимым, что и приводит к ошибке при компиляции. Синтаксис объявления метода arraysEqual может быть обобщен. Ниже приведена общая форма объявления обобщенного метода.

<параметрытипа> возвращаемыйтип имя_метода (параметры) { // ... Как и при вызове обычного метода, параметры_типа разделяются запятыми. В обобщенном методе их список предваряет возвращаемый_тип. ## Обобщенные конструкторы Конструктор может быть обобщенным, даже если сам класс не является таковым. Например, в приведенной ниже программе класс Summation не является обобщенным, но в нем используется обобщенный конструктор.

// Применение обобщенного конструктора, class Summation { private int sum; // Обобщенный конструктор. <T extends Number> Summation(T arg) { sum = 0; for(int i=0; i <= arg.intValue; i++) sum += i; } int getSum { return sum; }

Поделиться:
Популярные книги

Черный маг императора 3

Герда Александр
3. Черный маг императора
Фантастика:
попаданцы
аниме
5.00
рейтинг книги
Черный маг императора 3

Повелитель механического легиона. Том VIII

Лисицин Евгений
8. Повелитель механического легиона
Фантастика:
технофэнтези
аниме
фэнтези
5.00
рейтинг книги
Повелитель механического легиона. Том VIII

Пипец Котенку! 3

Майерс Александр
3. РОС: Пипец Котенку!
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Пипец Котенку! 3

Разбуди меня

Рам Янка
7. Серьёзные мальчики в форме
Любовные романы:
современные любовные романы
остросюжетные любовные романы
5.00
рейтинг книги
Разбуди меня

Боги, пиво и дурак. Том 6

Горина Юлия Николаевна
6. Боги, пиво и дурак
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
Боги, пиво и дурак. Том 6

Болотник 2

Панченко Андрей Алексеевич
2. Болотник
Фантастика:
попаданцы
альтернативная история
6.25
рейтинг книги
Болотник 2

Ты всё ещё моя

Тодорова Елена
4. Под запретом
Любовные романы:
современные любовные романы
7.00
рейтинг книги
Ты всё ещё моя

S-T-I-K-S. Пройти через туман

Елисеев Алексей Станиславович
Вселенная S-T-I-K-S
Фантастика:
боевая фантастика
7.00
рейтинг книги
S-T-I-K-S. Пройти через туман

Имя нам Легион. Том 4

Дорничев Дмитрий
4. Меж двух миров
Фантастика:
боевая фантастика
рпг
аниме
5.00
рейтинг книги
Имя нам Легион. Том 4

Сводный гад

Рам Янка
2. Самбисты
Любовные романы:
современные любовные романы
эро литература
5.00
рейтинг книги
Сводный гад

Я князь. Книга XVIII

Дрейк Сириус
18. Дорогой барон!
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Я князь. Книга XVIII

Королевская Академия Магии. Неестественный Отбор

Самсонова Наталья
Любовные романы:
любовно-фантастические романы
8.22
рейтинг книги
Королевская Академия Магии. Неестественный Отбор

Последняя Арена 6

Греков Сергей
6. Последняя Арена
Фантастика:
рпг
постапокалипсис
5.00
рейтинг книги
Последняя Арена 6

Жребий некроманта. Надежда рода

Решетов Евгений Валерьевич
1. Жребий некроманта
Фантастика:
фэнтези
попаданцы
6.50
рейтинг книги
Жребий некроманта. Надежда рода