Выбрать главу

// определить равенство абсолютных значений в двух объектах boolean absEqual(NumericFns<?> ob) { // обратите внимание на метасимвол if(Math.abs(num.doubleValue()) == Math.abs(ob.num.doubleValue()) return true; return false; } В данном случае выражение NumericFns<?> соответствует любому типу объекта из класса NumericFns и позволяет сравнивать абсолютные значения в двух произвольных объектах класса NumericFns. Ниже приведен пример программы, демонстрирующий применение метасимвольного аргумента.

// Применение метасимвольного аргумента, class NumericFns { T num; // передать конструктору ссылку на числовой объект NumericFns(Т п) { num = п; } // возвратить обратную величину double reciprocal() { return 1 / num.doubleValue(); } // возвратить дробную часть double fraction() { return num.doubleValue() - num.intValue(); } // определить равенство абсолютных значений в двух объектах boolean absEqual(NumericFns<?> ob) { if(Math.abs(num.doubleValue()) == Math.abs(ob.num.doubleValue())) return true; return false; } // ...

} // продемонстрировать применение метасимвольного аргумента class WildcardDemo { public static void main(String args[]) { NumericFns iOb = new NumericFns(6) ; NumericFns dOb = new NumericFns(-6.0) ; NumericFns 10b = new NumericFns(5L); System.out.println("Testing iOb and dOb."); // В этом вызове метода тип метасимвольного // аргумента совпадает с типом Double. if(iOb.absEqual(dOb)) System.out.println("Absolute values are equal."); else System.out.println("Absolute values differ."); System.out.println(); System.out.println("Testing iOb and 10b."); // А в этом вызове метода тип метасимвольного // аргумента совпадает с типом Long. if(iOb.absEqual(10b)) System.out.println("Absolute values are equal."); else System.out.println("Absolute values differ."); }

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

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, в котором определяется, содержатся ли в двух массивах одинаковые элементы, расположенные в том ж самом порядке. Такой метод можно использовать для сравнения двух массивов одинаковых или совместимых между собой типов.