Java: руководство для начинающих (ЛП) - Шилдт Герберт
Шрифт:
Интервал:
Закладка:
NumericFns dOb = new NumericFns(1.25) ;NumericFns fOb = new NumericFns(-1.25) ;
if(dOb.absEqual(fOb)) System.out.println("Absolute values are the same.");else System.out.println("Absolute values differ.");На первый взгляд может показаться, что при выполнении метода absEqual () не должно возникнуть никаких затруднений, но это совсем не так. Затруднения начнутся при первой же попытке объявить параметр типа NumericFns. Каким он должен быть? Казалось бы, подходящим должно быть следующее решение, где т указывается в качестве параметра типа:
//Не пройдет!
// определить равенство абсолютных значений в двух объектахboolean absEqual(NumericFns ob) { if(Math.abs(num.doubleValue()) == Math.abs(ob.num.doubleValue()) return true; return false;}В данном случае для определения абсолютного значения каждого числа используется стандартный метод Math. abs (). Полученные значения сравниваются. Но дело в том, что рассматриваемое здесь решение окажется пригодным лишь в том случае, если объект класса NumericFns, передаваемый в качестве параметра, имеет тот же тип, что и текущий объект. Так, если текущий объект относится к типу NumericFns<Integer>, параметр ob также должен быть типа NumericFns<Integer>, а следовательно, сравнить текущий объект с объектом типа NumericFns<Double> не удастся. Таким образом, выбранное решение не является обобщенным.Для того чтобы создать обобщенный метод absEqual (), придется воспользоваться еще одним свойством обобщений в Java, называемым метасимвольным аргументом. Для указания такого аргумента служит знак ?, обозначающий неизвестный тип данных. Используя метасимвольный аргумент, можно переписать метод absEqual () следующим образом:
// определить равенство абсолютных значений в двух объектах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 в качестве параметра.В целом верхняя граница для метасимвольного аргумента задается в следующей общей форме: